sgcSign を 5 分で
ドキュメントへの署名は 2 つのコンポーネントで行います。署名コンポーネントとキープロバイダーです。このページでは、Windows 証明書ストアの証明書を使って PDF に PAdES 署名を行い、作成したものを検証する方法を示します。XML に署名したい場合は、下にリンクされているより詳しい XAdES のチュートリアルがあります。
ドキュメントへの署名は 2 つのコンポーネントで行います。署名コンポーネントとキープロバイダーです。このページでは、Windows 証明書ストアの証明書を使って PDF に PAdES 署名を行い、作成したものを検証する方法を示します。XML に署名したい場合は、下にリンクされているより詳しい XAdES のチュートリアルがあります。
署名コンポーネントはドキュメントの形式を知っています。キープロバイダーは秘密鍵がどこにあるかを知っています。両者は 1 つのプロパティで結び付きます。
TsgcPAdESSigner は sgcSign_PAdES.pas で宣言され、SGC Sign パレットページに登録されています。SignPDFFile は入力パスと出力パスを受け取ります。
Windows 証明書ストアには TsgcWindowsCertStoreProvider、.pfx ファイルには TsgcPFXKeyProvider を使用します。どちらも同じパレットページにあります。
KeyProvider で、その型はコンポーネント参照ではなくインターフェース IsgcKeyProvider です。この違いは有効期間に関わるため重要で、その理由は下の問題の項で説明します。
Win32、Win64、Linux64、Intel および Apple Silicon の macOS、iOS、Android です。Windows ではハッシュと署名は Windows CNG API を通じて行われ、それ以外のすべてのプラットフォームではライブラリ独自の純粋な Pascal の暗号処理で行われるため、配布する OpenSSL はありません。Windows 証明書ストアのプロバイダーは、Windows 専用のままである唯一のコンポーネントです。
sgcSign には機能による階層がないため、この表はエディションではなく、コンパイラーとプラットフォームについてのものです。
| 項目 | 内容 |
|---|---|
| IDE | Delphi 7 から RAD Studio 13、および C++Builder。C++Builder では、lib フォルダーをライブラリパスではなくシステムインクルードパスに追加します。 |
| uses 句 | デモは sgcSign_Types, sgcSign_Interfaces, sgcSign_Classes, sgcSign_PAdES, sgcSign_KeyProvider_WinCertStore, sgcSign_KeyProvider_PEM, sgcSign_KeyProvider_PFX と記述しています。使用しないプロバイダーは省いてください。 |
| エディション | ありません。製品固有の sgcVer.inc には SGC_EDT_* の define がまったくなく、階層によって制限される機能もありません。1 つのライブラリで、すべてのコンポーネントが、すべてのライセンスに含まれます。商用の階層はシート数によるもので、シングル、チーム、サイト、そして無料の Community Edition があります。 |
| プラットフォーム(ソースで確認済み) | Win32、Win64、Linux64、OSX64、OSXARM64、iOS、Android です。暗号処理は 1 つのシームユニット sgcSign_Crypto.pas を通じて行われ、Windows では CNG、それ以外ではすべて純粋な Pascal です。HTTP は Windows では WinHTTP、それ以外では Delphi RTL のクライアントを使用し、実行時パッケージは、Linux64 と Intel の macOS を Delphi 10.3 から、Android と iOS を 10.4 から、Apple Silicon の macOS を 11 から有効にします。sgcSign_KeyProvider_WinCertStore.pas は Windows 専用の唯一のユニットです。 |
| 外部依存 | ありません。Windows ではライブラリが Windows の CNG と WinHTTP の API を直接呼び出し、他のプラットフォームでは独自の純粋な Pascal の暗号処理と Delphi RTL の HTTP クライアントを使用するため、アプリケーションと一緒に配布する OpenSSL の DLL はありません。 |
| 既定値 | 新しい TsgcPAdESSigner には、すでに使用可能なプロファイルが設定されています。コンストラクターが基本の PAdES プロファイル、ベースライン B の署名レベル、SHA-256 を設定します。有効な署名を作成するために Profile に触れる必要はありません。 |
PDF より XML がよい場合は、5 分間の XAdES チュートリアルで、PFX ファイルを使って XML ドキュメントに署名し、Delphi 7 の Unicode の落とし穴と UTC の署名時刻も扱っています。このページは、その PDF と証明書ストア版です。
デザイン時パッケージは実行時パッケージを参照するため、先に実行時パッケージをコンパイルしてください。
ダウンロードしたファイルをフォルダーに展開します。以下ではこのフォルダーを {$DIR} と呼びます。
ツール、環境オプション、ディレクトリの順に開きます。すべての RAD Studio バージョンに適用される {$DIR}\delphi\source を追加します。
バージョン固有のフォルダーも追加します。たとえば RAD Studio 13 では {$DIR}\delphi\libD13\$(Platform)、Delphi 7 では libD7 までです。C++Builder では、これらを代わりにシステムインクルードパスに追加します。
お使いの IDE バージョン用の Packages\sgcSignD13.groupproj、C++Builder では sgcSignC13.groupproj を開きます。最初に sgcSign パッケージをコンパイルし、次に dclsgcSign をインストールします。
SGC Sign というページが表示され、署名コンポーネント、検証コンポーネント、タイムスタンプと OCSP のクライアント、10 種類のキープロバイダーが含まれます。Windows では Authenticode の署名と検証のコンポーネントも含まれます。
証明書を選び、署名コンポーネントを作成し、プロバイダーに向けて SignPDFFile を呼び出します。1 つ目のタブは Windows 証明書ストア、2 つ目は PFX ファイルを使用します。
uses
SysUtils, Classes,
// sgcSign
sgcSign_Types, sgcSign_Interfaces, sgcSign_Classes,
sgcSign_PAdES, sgcSign_KeyProvider_WinCertStore;
procedure TFormMain.btnSignClick(Sender: TObject);
var
vSigner: TsgcPAdESSigner;
vKeyProvider: TsgcWindowsCertStoreProvider;
vProviderIntf: IsgcKeyProvider;
vOutputFile: string;
begin
vKeyProvider := TsgcWindowsCertStoreProvider.Create(nil);
vSigner := TsgcPAdESSigner.Create(nil);
try
vKeyProvider.SelectCertificateBySubject('My Company');
Log('Certificate found: ' + vKeyProvider.Certificate.Subject);
// Hold the interface in an explicit local: an inline "as" cast
// would leave a compiler-generated interface temporary alive in
// this stack frame until the routine returns, i.e. past
// vKeyProvider.Free, and releasing it would touch freed memory.
vProviderIntf := vKeyProvider as IsgcKeyProvider;
vSigner.KeyProvider := vProviderIntf;
vSigner.Reason := 'Demo signature';
vSigner.Location := 'Spain';
vSigner.SignerName := 'sgcSign Demo';
vOutputFile := ChangeFileExt(edInputFile.Text, '_signed.pdf');
vSigner.SignPDFFile(edInputFile.Text, vOutputFile);
Log('SUCCESS: PDF signed with PAdES profile');
Log('Output file: ' + vOutputFile);
finally
vSigner.Free;
vProviderIntf := nil;
vKeyProvider.Free;
end;
end;
ここにないものに注目してください。コンストラクターがすでに基本の PAdES プロファイル、ベースライン B の署名レベル、SHA-256 を設定しているため、Profile には一切触れていません。インターフェースの一時オブジェクトに関するコメントは同梱のデモ自身のもので、finally ブロックでの解放の順序が、それが重要な理由です。
uses
SysUtils, Classes,
// sgcSign
sgcSign_Types, sgcSign_Interfaces, sgcSign_Classes,
sgcSign_PAdES, sgcSign_KeyProvider_PFX;
var
vSigner: TsgcPAdESSigner;
vKeyProvider: TsgcPFXKeyProvider;
vProviderIntf: IsgcKeyProvider;
begin
vKeyProvider := TsgcPFXKeyProvider.Create(nil);
vSigner := TsgcPAdESSigner.Create(nil);
try
vKeyProvider.FileName := 'C:\certs\signer.pfx';
vKeyProvider.Password := GetPfxPassword;
vKeyProvider.LoadFromFile;
Log('Certificate loaded (PFX): ' + vKeyProvider.Certificate.Subject);
vProviderIntf := vKeyProvider as IsgcKeyProvider;
vSigner.KeyProvider := vProviderIntf;
vSigner.SignPDFFile('C:\docs\contract.pdf',
'C:\docs\contract_signed.pdf');
finally
vSigner.Free;
vProviderIntf := nil;
vKeyProvider.Free;
end;
end;
1 つ目のタブとの唯一の違いは、どのプロバイダーを作成し、どのように鍵を指定するかです。KeyProvider := 以降はすべて同じで、これは PKCS#11 ハードウェアやクラウドの鍵サービスを含む、10 種類すべてのプロバイダーに当てはまります。
uses
SysUtils, Classes,
// sgcSign
sgcSign_Types, sgcSign_Interfaces, sgcSign_Verifier;
var
oVerifier: TsgcSignatureVerifier;
vVerifier: IsgcSignatureVerifier;
oStream: TFileStream;
begin
oVerifier := TsgcSignatureVerifier.Create(nil);
oStream := TFileStream.Create('C:\docs\contract_signed.pdf',
fmOpenRead or fmShareDenyWrite);
try
// VerifyPDF is reached through the interface the component implements
vVerifier := oVerifier as IsgcSignatureVerifier;
if vVerifier.VerifyPDF(oStream) = vsValid then
Writeln('valid')
else
Writeln(oVerifier.GetVerificationDetails);
finally
vVerifier := nil;
oStream.Free;
oVerifier.Free;
end;
end;
署名コンポーネントと同じインターフェースのルールです。キャストを名前付きのローカル変数に代入し、コンポーネントを解放する前にそれをクリアしてください。真偽値では証拠として不十分な場合は、GetValidationReportXML が ETSI 検証レポート形式のレポートを作成します。
最初の 2 つのタブは、同梱のデモ Demos\Delphi\PAdES\frmMain.pas から、分岐をタブごとに 1 つの経路にまとめたものです。インターフェースを名前付きのローカル変数に保持することについてのコメントはデモ自身のもので、残しておく価値があります。より充実した姉妹デモ Demos\Delphi\PAdES_Providers では、ハードウェアとクラウドのプロバイダーで同じことを行います。
ファイルができたことは、署名が検証できることと同じではありません。
デモと同様に、選択後に Certificate.Subject を読み取ってください。意図した証明書で署名したのか、最初に一致したものがたまたま使われたのかの違いが分かります。IsLoaded は同じ問いに真偽値で答えます。
SignPDFFile は指定した出力パスに書き込みます。同梱のデモは ChangeFileExt でそれを導出しているため、署名済みファイルは元のファイルの隣に作成されます。
確認すべき戻り値はないため、呼び出しを try except に入れて例外メッセージを読み取ってください。デモがそうしており、それが唯一の失敗の経路です。
ファイルは有効な署名とは限りません。TsgcSignatureVerifier.VerifyPDF は TsgcVerificationStatus を返し、それを vsValid と比較します。GetVerificationDetails は失敗を説明します。PDF リーダーでファイルを開けば、人にも同じことが分かります。
最初の署名で起きる問題のほとんどは、6 つのいずれかです。
デモ自身のコメントが警告しているものです。KeyProvider は IsgcKeyProvider を受け取るため、インラインの as キャストはコンパイラーが生成したインターフェース参照を、ルーチンが戻るまで、つまりプロバイダーコンポーネントを解放した後まで残します。インターフェースを名前付きのローカル変数に代入し、署名コンポーネント、次にインターフェースを nil、最後にプロバイダーの順に解放してください。
TsgcSignProfileConfig オブジェクトです。Profile := 'something' ではなく、Profile.Profile と Profile.SignatureLevel を設定します。コンストラクターがすでに使用可能な既定値を設定しているため、最初のサンプルではまったく触れる必要がありません。
SelectCertificateBySubject はサブジェクトで、SelectCertificateByThumbprint は拇印で一致させます。デモと同様に、選択後に Certificate.Subject を読み取り、実際にどの証明書が選ばれたかを確認してください。EnumerateCertificates で利用可能なものを一覧できます。
TsgcWindowsCertStoreProvider(sgcSign_KeyProvider_WinCertStore.pas 内)は、Windows でのみビルドされる唯一のユニットです。Linux、macOS、iOS、Android では、PFX、PEM、PKCS#11、クラウドプロバイダーなど別のキープロバイダーを選んでください。ライブラリの残りの部分は同じように動作します。
基本的な署名には、トラストアンカーも失効データも含まれません。TSAClient でタイムスタンプを追加し、証明書の期限切れ後もドキュメントを検証可能にする必要がある場合は、長期のプロファイルに移行してください。
それが設計です。確認すべき戻り値はないため、呼び出しを try except に入れて例外メッセージを読み取ってください。同梱のデモがそうしています。
4 つの方向があり、いずれも同じライブラリの中にあります。
XML 用の XAdES と XMLDSig、デタッチ CMS 用の CAdES、ASiC コンテナー、ClickOnce、NuGet、VSIX パッケージ用の専用署名コンポーネントがあります。Windows では Authenticode の署名コンポーネントもあります。
10 種類のキープロバイダーが同梱されています。PFX、PEM、Windows ストア、PKCS#11 ハードウェア、Azure Trusted Signing、AWS KMS、Google Cloud KMS、Certum SimplySign、HashiCorp Vault、CSC リモート署名プロトコルです。
スペインの VeriFactu から EU の請求書形式まで、21 の国別および業界別のプロファイルがあり、それぞれの制度が求めるフィールドと署名レベルを備えています。
sgcSign Server は、鍵を保持してリクエストに応じて署名するセルフホスト型のデーモンで、証明書が信頼しているマシンの外に出ることはありません。
デモプロジェクトはダウンロードに含まれており、Demos\Delphi の下にあります。このページは PAdES デモを基にしています。
| 5 分間の XAdES チュートリアル 詳しいクイックスタートです。新しい VCL プロジェクト、PFX ファイル、署名済みの XML エンベロープを扱います。 | 開く | |
| キープロバイダー 秘密鍵を置ける 10 か所すべてと、それぞれに必要なものです。 | 開く | |
| 署名プロファイル 21 の国別および業界別のプロファイルと、それぞれの要件です。 | 開く | |
| PDF 署名のチュートリアル 可視署名を含む、PAdES のより詳しい解説です。 | 開く | |
| sgcSign Server 鍵を移動させてはならない場合のための、セルフホスト型の署名デーモンです。 | 開く | |
| 体験版のダウンロード 製品版と同じインストーラーで、期間限定です。無料の Community Edition もあります。 | 開く |
関連資料:sgcSign の紹介とコード署名サーバー。各製品にはそれぞれクイックスタートがあり、はじめにのページに一覧があります。
sgcSign_PAdES.pas で宣言されている TsgcPAdESSigner と、キープロバイダーです。Windows 証明書ストアの場合は、sgcSign_KeyProvider_WinCertStore.pas の TsgcWindowsCertStoreProvider です。.pfx ファイルの場合は、sgcSign_KeyProvider_PFX.pas の TsgcPFXKeyProvider です。どちらも SGC Sign パレットページにあります。プロバイダーを署名コンポーネントの KeyProvider プロパティに代入してから、SignPDFFile(aInputFile, aOutputFile) を呼び出します。
KeyProvider はコンポーネントではなくインターフェース IsgcKeyProvider として型付けされているためです。インラインの as キャストは、コンパイラーが生成するインターフェースの一時オブジェクトを作成し、それはルーチンが戻るまでスタックフレームに残ります。これはプロバイダーコンポーネントが解放された後であり、その時点で解放すると、すでに存在しないメモリに触れることになります。デモはキャストを名前付きのローカル変数に代入し、署名コンポーネント、インターフェースを nil、プロバイダーの順に解放しています。その順序をそのまま使用してください。
sgcVer.inc には SGC_EDT_* の define がまったくなく、階層によって制限されるコンポーネントや形式もありません。すべてのライセンスに、すべての署名コンポーネント、すべてのキープロバイダー、すべての国別プロファイルが含まれます。商用の階層はシート数によるもので、シングル、チーム、サイトがあり、体験版とは別に無料の Community Edition があります。
Profile は TsgcSignProfileConfig オブジェクトなので、文字列を代入するのではなく Profile.Profile と Profile.SignatureLevel を設定します。長期署名の場合は、LTV プロファイルとベースライン LT レベルに移行し、TSAClient を接続してください。
TsgcSignatureVerifier を使用します。VerifyPDF はストリームを受け取り、TsgcVerificationStatus を返すので、それを vsValid と比較します。GetVerificationDetails は失敗を説明し、真偽値ではなく証拠が必要な場合は、GetValidationReportXML が ETSI 検証レポート形式のレポートを作成します。