チュートリアル: Delphi での XAdES XML 署名

プレーンな XML 請求書から、FatturaPA、FACTUR-X、欧州中のほとんどの電子請求書ポータルが要求するレベルである XAdES-B-T ドキュメントを生成する実践的ウォークスルー。enveloped、enveloping、detached の署名パッケージングから選び、RFC 3161 タイムスタンプを添付し、結果を検証することで、取引先が何を見るかを正確に把握できます。

XAdES-B-B / B-T / B-LT / B-LTA
Enveloped / Enveloping / Detached
Delphi 7 – RAD Studio 13

3 つのパッケージングモード、1 つのエンジン

XAdES は W3C XML-DSig 仕様をタイムスタンピングと長期検証データで拡張します。何かに署名する前に、ds:Signature 要素がどこに住むかを決めてください。

Enveloped

ds:Signature 要素はドキュメントルートの内側に追加されます。これは FatturaPA、FACTUR-X-XAdES、ほとんどの電子請求書フローで使用される形式です。署名済み XML は依然として元のドキュメントの有効なコピーです — リーダーは理解しない署名要素を無視します。

Enveloping

元の XML が ds:Signature内側ds:Object 子になります。単一の自己完結型エンベロープが必要で、既存のコンシューマーがドキュメントをパースし続けられるかどうかを気にしない場合に便利。

Detached

署名は別ファイルに置かれ、URI 参照経由で元のドキュメントを指します。SAML、ebXML、ソースドキュメントがバイト単位で変更されてはならない任意のフローで一般的。

ユニットを追加する

XAdES には XML 署名者、XAdES プロファイル、鍵プロバイダーが必要です。このチュートリアルでは PFX を使用。スマートカードやクラウド HSM がある場合は 10 のプロバイダーのいずれかに置き換えてください。

Delphi の uses

sgcSign_XML は XML 署名コアです。sgcSign_Profile_XAdES はレベル、変換、タイムスタンプ構成を公開します。sgcSign_KeyProvider_PFX はディスクから PKCS#12 ファイルを読み込みます — 最も一般的な出発点。

長期レベル(B-LT、B-LTA)には、失効データを取得して埋め込むために sgcSign_OCSPsgcSign_CRL も必要です。

uXAdESSigning.pas
uses
  Classes, SysUtils,
  // sgc
  sgcSign_KeyProvider_PFX,
  sgcSign_XML,
  sgcSign_Profile_XAdES,
  sgcSign_TSA,
  sgcSign_OCSP,
  sgcSign_CRL,
  sgcSign_Verifier;

XML と鍵を読み込む

2 つの入力 — 署名したい XML と署名に使う PFX 証明書。

PFX 鍵プロバイダー

TsgcPFXKeyProvider は Windows CNG 経由で PKCS#12 ファイルをインポートします。これにより、証明書が元々どの CSP 向けに発行されたかに関係なく、モダンな署名ハンドルが得られます。同じプロバイダーは Windows 7 以降、32-bit と 64-bit で動作します。

プロバイダーを完全な署名操作中に生かしておいてください — XML 署名者は SignXML が戻るまで基盤の鍵ハンドルを参照します。

step1-load.pas
var
  vXML: string;
  vKeyProvider: TsgcPFXKeyProvider;
begin
  vXML := TFile.ReadAllText('invoice.xml', TEncoding.UTF8);

  vKeyProvider := TsgcPFXKeyProvider.Create(nil);
  vKeyProvider.FileName := 'certificate.pfx';
  vKeyProvider.Password := 'secret';
  vKeyProvider.LoadFromFile;
end;

XAdES-B-T または XAdES-B-LT を選ぶ

B-T は署名タイムスタンプを追加。B-LT はさらに CA チェーンと失効データを埋め込むため、署名は何年も検証可能なまま保たれます。

プロファイル構成

Level は AdES レベルを選びます: xalBxalTxalLTxalLTAPackagingxpkEnveloped(デフォルト)、xpkEnvelopingxpkDetached を選びます。

Transforms リストはデフォルトで xtEnvelopedSignature + xtC14NExclusive で、ほとんどの電子請求書スキーマが必須とするものです。特定の国別プロファイルが包括的 C14N やカスタム XSLT 変換を要求する場合のみオーバーライドしてください。

B-LT では、OCSP.AutoFetch := True により、署名者がチェーン内のすべての証明書の OCSP レスポンスを取得し、RevocationValues 要素内に埋め込むよう指示します。署名済み XML は、検証者が必要とするすべてを運ぶようになります — 検証時のネットワーク呼び出しは不要。

step2-profile.pas
var
  vProfile: TsgcSignProfile_XAdES;
begin
  vProfile := TsgcSignProfile_XAdES.Create(nil);
  vProfile.Level := xalT;            // or xalLT, xalLTA
  vProfile.Packaging := xpkEnveloped;
  vProfile.HashAlgorithm := shaSHA256;

  // Timestamp authority for B-T and above
  vProfile.TSA.URL := 'https://freetsa.org/tsr';
  vProfile.TSA.HashAlgorithm := shaSHA256;

  // For B-LT: embed full chain and revocation data
  vProfile.OCSP.AutoFetch := True;
  vProfile.CRL.AutoFetch := True;
end;

署名 — enveloped、enveloping、detached

同じ TsgcSignXML コンポーネントが 3 つのパッケージングモードすべてを処理します — Packaging プロパティのみが異なります。

Enveloped 署名

請求書のデフォルト。署名はドキュメントルート内に追加され、enveloped-signature 変換を使用してダイジェストから自身を除外します。

スキーマ対応ペイロード(FatturaPA、TicketBAI、KSeF)では、署名が正しい XML 名前空間を継承するよう RootNamespace を設定してください — 国別プロファイルが自動的にこれを設定します。

step3-enveloped.pas
var
  vSigner: TsgcSignXML;
  vSigned: string;
begin
  vSigner := TsgcSignXML.Create(nil);
  try
    vSigner.KeyProvider := vKeyProvider;
    vSigner.Profile := vProfile;          // Packaging = xpkEnveloped
    vSigned := vSigner.SignXML(vXML);
    TFile.WriteAllText('invoice-signed.xml', vSigned, TEncoding.UTF8);
  finally
    vSigner.Free;
  end;
end;

Enveloping と detached

プロファイルのパッケージングを切り替え、それ以外は同じに保ちます。detached 署名では、DetachedURI を署名されるドキュメントのパスまたは URL に設定してください。検証者がデータを取得するためにその参照を必要とします。

detached 署名を受け取ったときは、署名 XML と元のドキュメントバイトの両方を VerifyDetached に渡します — sgcSign がダイジェストを再計算し、バインディングを確認します。

step3-other.pas
// Enveloping: original XML wrapped in a ds:Object
vProfile.Packaging := xpkEnveloping;
vSigned := vSigner.SignXML(vXML);

// Detached: signature file points at invoice.xml
vProfile.Packaging := xpkDetached;
vProfile.DetachedURI := 'invoice.xml';
vSigned := vSigner.SignXML(vXML);
TFile.WriteAllText('invoice.sig.xml', vSigned, TEncoding.UTF8);

署名済み XML を検証する

すべての署名済みドキュメントは、ワークフローを出荷する前に検証しなければなりません。

1 回の呼び出し、完全なレポート

VerifyXML は検出された AdES レベル、署名者サブジェクト、署名タイムスタンプ、チェーンや失効問題を持つ TsgcSignatureReport を返します。enveloped 署名では、検証者が自動的に ds:Signature 要素を見つけます。detached 署名では VerifyDetached を使用し、元のドキュメントバイトを供給してください。

StatussvValid ならダイジェストは一致し、証明書チェーンは信頼されたルートに固定され、タイムスタンプは無傷です。StatusDetail に理由が入った svInvalid が典型的な失敗モード。svUnknown は検証者が OCSP レスポンダーまたは CRL 配布ポイントに到達できなかったことを意味します。

step4-verify.pas
var
  vVerifier: TsgcSignatureVerifier;
  vReport: TsgcSignatureReport;
begin
  vVerifier := TsgcSignatureVerifier.Create(nil);
  try
    vReport := vVerifier.VerifyXML(vSigned);
    Memo1.Lines.Add('Level:   ' + vReport.Signatures[0].LevelAsString);
    Memo1.Lines.Add('Status:  ' + vReport.Signatures[0].StatusAsString);
    Memo1.Lines.Add('Signer:  ' + vReport.Signatures[0].Subject);
    Memo1.Lines.Add('TSA:     ' +
      DateTimeToStr(vReport.Signatures[0].TimestampUTC));
  finally
    vVerifier.Free;
  end;
end;

通常何がうまくいかないか

開発者が最初の XAdES エンベロープに署名するときに最もよく見る問題。

空白と正規化

署名済み XML をきれいに整形すること — テキストエディタ、IDE、または XSLT を通すこと — はダイジェストを壊します。Exclusive C14N はあらゆるバイトに敏感です。署名済み XML はバイトとして保存し、バイトとして転送し、決して再整形しないでください。

入力ドキュメントの BOM

XML の先頭の UTF-8 BOM は、特にドキュメントが Notepad で書かれたときの、ダイジェスト不一致の頻繁な原因です。署名前に BOM を除去してください — sgcSign は内部で正規化しますが、他の検証者はしないかもしれません。

FatturaPA には xalLT が必要

イタリアの電子請求書ポータルは、アーカイブ時に長期失効ルックアップを実行できないため XAdES-B-T を拒否します。OCSP.AutoFetch = True 付きの xalLT を使用すると、OCSP レスポンスがドキュメントと共に旅します。

detached 署名と URI 解決

DetachedURI が相対パスの場合、検証者は自身の作業ディレクトリに対して解決します。マシン間のフローでは、絶対 URL を使うか、ドキュメントバイトを直接 VerifyDetached に渡してください。

ここからどこへ

国別プロファイルが XAdES の構成を代行します。サーバーはビルドファーム全体で署名を集約します。

国別プロファイル

VeriFactu、FatturaPA、KSeF、FACTUR-X、TicketBAI — 1 つの定数があらゆる変換、アルゴリズム、必須属性を切り替えます。

続きを読む →

PAdES チュートリアル

同じエンジンを XML ではなく PDF に適用。PAdES ウォークスルーを読んで 2 つの形式を並べて比較してください。

続きを読む →

sgcSign Server

REST API、GitHub Actions 統合、Docker と Helm。個々の開発者から制御されたサービスへ署名をオフロード。

続きを読む →

最初の XML に署名する準備はできましたか?

トライアルをダウンロードし、このチュートリアルを自分の請求書に対して実行し、出荷時に sgcSign のライセンスを取得してください。