PDFにさまざまな種類のデジタル署名を作成、管理、検証できます。
さまざまなローカルまたはオンラインのセキュアなプロバイダーからの電子証明書で法的および規制上の要件をサポートします。
この機能は「Pdftools SDK」ライブラリの一部です。
電子署名は電子文書またはメッセージの信頼性と整合性を確認するために使用される技術です。
電子署名は秘密鍵を使用して文書またはメッセージを暗号化することによって実施されます。
作成された電子署名は対応する公開鍵を使用して検証します。
この公開鍵は署名を検証したい人なら誰でも使用できるように認証局によって公開されています。
電子署名は文書またはメッセージがいかなる方法でも改ざんまたは変更されていないことや、送信者と主張する人物によって送信されたことを確認するために使用されます。
Pdftools SDKは次の三種類のデジタル署名をサポートしています:
文書承認署名は契約の承認や署名などのワークフローの一部としてPDFに電子署名を施します。
この電子署名は署名者のID(電子証明書)を記録し、署名の適用後に文書の内容が変更されていないことを確認できるようにします。
PDFには複数のこの文書承認署名を関連付けることができます。
たとえば、複数の当事者が契約に署名する場合、各文書承認署名にはユニークな電子署名があり、各署名は連続して適用され、署名チェーンが作成されます。
この署名は、文書作成者の ID を記録します。
これにより、ユーザーは署名の有効性を維持しながら、PDFに特定の変更を加えることができます。たとえば、作成者は、署名の有効性を維持しながら、ユーザーがドキュメントのフォーム に入力できるようにすることができます。
作成者が許可していない変更がドキュメントに行われた場合の署名は無効になります。
PDFには一つのMDPを含めることができ、ドキュメントに他の署名を追加する前にその署名を追加する必要があります。
MDPを含む一般的なワークフローは次のようになります:
ドキュメント認証署名には次の設定を使用できます:
タイムスタンプ署名は文書が特定の時間に存在したことと、その時間以降文書の内容が変更されていないことを証明します。
多くの場合、タイムスタンプ署名は以前に署名された文書に変更がないことを確認するための「再署名」に使用されます。
タイムスタンプは独立した信頼できるタイムスタンプ機関(Time-Stamp Athority)によって提供されます。
暗号化プロバイダーは、証明書と関連する秘密キーを管理し、暗号化アルゴリズムを実装します。
Pdftools SDK は以下の暗号化プロバイダーをサポートしています。
Pdftools SDK を使用すると様々な既定に従ってPDFの承認、PDFへの電子署名と検証、およびタイムスタンプ署名と検証ができます。
署名は証明書、オンライン証明書ステータス プロトコル(OCSP)の結果や証明書失効リスト(CRL)などのソースを使用して検証できます。
これらのソースはPDFファイルに埋め込むことで長期検証を可能にします。また、ローカル マシンに保存したり、発行者からダウンロードしたりできます。
検証プロセスではすべての署名とそれに対応する詳細のリストを返すことができ、イベントを使用して特定のビジネス ロジックをトリガーすることもできます。
電子署名に視覚的な外観(スタンプや印影など)を追加できます。
これは、ドキュメント承認署名、ドキュメント認証署名、タイムスタンプ署名、および既存の未署名署名への署名に適用されます。
C#のサンプルプロジェクトではPdftools SDKライブラリ(DLL)をNuGetから自動でダウンロードします。
CのサンプルプロジェクトにはPdftools SDKライブラリ(DLL)が含まれています。
ライセンスキー無しで試用できます。ただし、結果に「透かし」が入ります。
「透かし」の削除をご希望の場合は問い合わせページまたはメールでお問い合わせください。
License Agreement(利用許諾契約書)が含まれていますので必ず確認してください。
視覚的な外観を持つドキュメント署名を追加します。
視覚的な外観はXML(またはjson)ファイルで指定します。外観にはテキスト、画像、またはPDFを追加できます。
この署名は可視部分と不可視部分で構成され、不可視部分のみで文書の署名部分を検証し、署名者のIDを認証できます。
署名用の証明書はパスワードで保護されたPKCS#12ファイル(.pfxまたは.p12)で指定します。
static void Sign(string certificateFile, string password, string appConfigFile, string inPath, string outPath) { // 組み込みの暗号化プロバイダへのセッションを生成 using var session = new BuiltIn.Provider(); // 証明書ファイルを開く using var pfxStr = File.OpenRead(certificateFile); // .pfx(または.p12)ファイルから署名設定を作成 BuiltIn.SignatureConfiguration signature = session.CreateSignatureFromCertificate(pfxStr, password); // XMLまたはjsonファイルから外観を作成 using var appStream = File.OpenRead(appConfigFile); if (Path.GetExtension(appConfigFile) == ".xml") signature.Appearance = Appearance.CreateFromXml(appStream); else signature.Appearance = Appearance.CreateFromJson(appStream); signature.Appearance.PageNumber = 1; signature.Appearance.CustomTextVariables.Add("company", "Daily Planet"); // 入力のPDFファイルを開く using var inStr = File.OpenRead(inPath); using var inDoc = Document.Open(inStr); // 出力ファイルのストリームを生成 using var outStr = File.Create(outPath); // 入力PDF文書に電子署名 using var outDoc = new Signer().Sign(inDoc, signature, outStr); }
入力PDF文書に含まれるすべての電子署名の署名情報を抽出して検証し、結果をコンソールに出力します。
// ヘルパー関数によって署名検証の詳細を出力します。
static int Validate(string inputFile, string certDir) { // 既定の検証プロファイルを使用します。 var profile = new Default(); // オフライン操作の場合はファイルシステムからカスタム信頼リスト(CustomTrustList)を構築し、外部失効チェックを無効にします。 if (certDir != null && certDir.Length != 0) { Console.WriteLine("Using 'offline' validation mode with custom trust list."); Console.WriteLine(); // 証明書を保持するためのカスタム信頼リストを生成 var ctl = new CustomTrustList(); // 証明書ディレクトリ内のファイルを反復処理し、カスタム信頼リストに証明書を追加 if (Directory.Exists(certDir)) { var directoryListing = Directory.EnumerateFiles(certDir); foreach (string fileName in directoryListing) { try { using var certStr = File.OpenRead(fileName); if (fileName.EndsWith(".cer") || fileName.EndsWith(".pem")) { ctl.AddCertificates(certStr); } else if (fileName.EndsWith(".p12") || fileName.EndsWith(".pfx")) { // パスワードが必要な場合はaddArchive(certStr, password)を使用します。 ctl.AddArchive(certStr); } } catch (Exception e) { Console.WriteLine("Could not add certificate '" + fileName + "' to custom trust list: " + e.Message); } } } else { // dirがディレクトリではない場合の処理 Console.WriteLine("Directory " + certDir + " is missing. No certificates were added to the custom trust list."); } Console.WriteLine(); // 検証プロファイルへのカスタム信頼リスト割り当て profile.CustomTrustList = ctl; // ファイルに埋め込まれた情報とカスタム信頼リストからの検証を許可 var vo = profile.ValidationOptions; vo.TimeSource = TimeSource.ProofOfExistence | TimeSource.ExpiredTimeStamp | TimeSource.SignatureTime; vo.CertificateSources = DataSource.EmbedInSignature | DataSource.EmbedInDocument | DataSource.CustomTrustList; // 失効チェックを無効にします。 profile.SigningCertTrustConstraints.RevocationCheckPolicy = RevocationCheckPolicy.NoCheck; profile.TimeStampTrustConstraints.RevocationCheckPolicy = RevocationCheckPolicy.NoCheck; } // 文書内のすべての署名を検証 (最新のものだけではありません。) var signatureSelector = SignatureSelector.All; // 検証オブジェクト(Validator)とイベントリスナーを作成 var validator = new Validator(); validator.Constraint += (s, e) => { Console.WriteLine(" - " + e.Signature.Name + (e.DataPart.Length > 0 ? (": " + e.DataPart) : "") + ": " + ConstraintToString(e.Indication, e.SubIndication, e.Message)); }; try { using var inStr = File.OpenRead(inputFile); // 入力のPDFファイルを開く // パスワードが必要な場合は、Open(inStr, password)を使用します。 using var document = Document.Open(inStr); // 文書、プロファイル、セレクタを検証メソッドに渡して実行 Console.WriteLine("Validation Constraints"); var results = validator.Validate(document, profile, signatureSelector); Console.WriteLine(); Console.WriteLine("Signatures validated: " + results.Count); Console.WriteLine(); // 結果を出力 foreach (var result in results) { var field = result.SignatureField; Console.WriteLine(field.FieldName + " of " + field.Name); try { Console.WriteLine(" - Revision : " + (field.Revision.IsLatest ? "latest" : "intermediate")); } catch (Exception ex) { Console.WriteLine("Unable to validate document Revision: " + ex.Message); } PrintContent(result.SignatureContent); Console.WriteLine(); } return 0; } catch (Exception ex) { Console.WriteLine("Unable to validate file: " + ex.Message); return 5; } }
private static void PrintContent(SignatureContent content) { if(content != null) { Console.WriteLine(" - Validity : " + ConstraintToString(content.Validity)); switch (content) { case UnsupportedSignatureContent: break; case CmsSignatureContent signature: { Console.WriteLine(" - Validation: " + signature.ValidationTime + " from " + signature.ValidationTimeSource); Console.WriteLine(" - Hash : " + signature.HashAlgorithm); Console.WriteLine(" - Signing Cert"); PrintContent(signature.SigningCertificate); Console.WriteLine(" - Chain"); foreach (var cert in signature.CertificateChain) { Console.WriteLine(" - Issuer Cert " + (signature.CertificateChain.IndexOf(cert) + 1)); PrintContent(cert); } Console.WriteLine(" - Chain : " + (signature.CertificateChain.IsComplete ? "complete" : "incomplete") + " chain"); Console.WriteLine(" Time-Stamp"); PrintContent(signature.TimeStamp); break; } case TimeStampContent timeStamp: { Console.WriteLine(" - Validation: " + timeStamp.ValidationTime + " from " + timeStamp.ValidationTimeSource); Console.WriteLine(" - Hash : " + timeStamp.HashAlgorithm); Console.WriteLine(" - Time : " + timeStamp.Date); Console.WriteLine(" - Signing Cert"); PrintContent(timeStamp.SigningCertificate); Console.WriteLine(" - Chain"); foreach (var cert in timeStamp.CertificateChain) { Console.WriteLine(" - Issuer Cert " + (timeStamp.CertificateChain.IndexOf(cert) + 1)); PrintContent(cert); } Console.WriteLine(" - Chain : " + (timeStamp.CertificateChain.IsComplete ? "complete" : "incomplete") + " chain"); break; } default: Console.WriteLine("Unsupported signature content type " + content.GetType().Name); break; } } else { Console.WriteLine(" - null"); } }
private static void PrintContent(Certificate cert) { if(cert != null) { Console.WriteLine(" - Subject : " + cert.SubjectName); Console.WriteLine(" - Issuer : " + cert.IssuerName); Console.WriteLine(" - Validity : " + cert.NotBefore + " - " + cert.NotAfter); try { Console.WriteLine(" - Fingerprint: " + FormatSha1Digest(new BigInteger(SHA1.Create().ComputeHash(cert.RawData)).ToByteArray(), "-")); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine(" - Source : " + cert.Source); Console.WriteLine(" - Validity : " + ConstraintToString(cert.Validity)); } else { Console.WriteLine(" - null"); } }
private static String ConstraintToString(ConstraintResult constraint) { return ConstraintToString(constraint.Indication, constraint.SubIndication, constraint.Message); }
private static String ConstraintToString(Indication indication, SubIndication subIndication, String message) { return (indication == Indication.Valid ? "" : (indication == Indication.Indeterminate ? "?" : "!")) + "" + subIndication + " " + message; }
// SHA-1ダイジェスト文字列を生成するヘルパー関数 private static String FormatSha1Digest(byte[] bytes, String delimiter) { var result = new StringBuilder(); foreach (byte aByte in bytes) { int number = (int)aByte & 0xff; String hex = number.ToString("X2"); result.Append(hex.ToUpper() + delimiter); } return result.ToString().Substring(0, result.Length - delimiter.Length); }
質問のページからお送りいただくようお願いします。
または、メールでsupport@trustss.co.jpあてにお送りください。
ご購入前の技術的質問も無償で対応します。サポート受付ページからお願いします。