[]
DioDocs for PDFでは、PDFドキュメントにデジタル署名を付加し、コンテンツの真正性を保護できます。 ライブラリでは、SignatureFieldを使用してデジタル署名を付加できます。また、タイムスタンプ付きのデジタル署名を付加し、PDFドキュメントの署名の日時をマークを付けることもできます。DioDocs for PDFでは、Time Stamp Authority(TSA)などの信頼できる機関によって作成された合法的なスタンプがサポートされています。デフォルトでドキュメントを増分更新するドキュメントに署名して保存するには、Signメソッドを使用できます。または、SaveMode列挙をIncrementalUpdateに設定し、それをパラメータとしてSignメソッドに渡すこともできます。どちらの方法も、元の署名を無効にすることなく、また元のコンテンツを変更することなく、ドキュメントに複数回署名することができます。 DioDocs for PDFでは、署名されたドキュメントに対して3つのレベルの後続の変更が可能です。
変更なし
フィールドを変更する
フィールドを変更し、注釈を追加する
ドキュメントに署名を付加された場合、新しいフィールドを追加すると既存の署名が無効になります。そのため、ドキュメントには、後続のすべての署名に収まるのに十分な署名フィールドがすでに含まれている必要があります。また、有効なライセンスキーなしで署名されたPDFを使用したサンプルを実行すると、生成されたPDF内の元の署名が無効になる現象が発生します。この現象は、元の署名されたドキュメントを変更するライセンスヘッダがPDFに追加されるために発生します。
さらに、DioDocs for PDFを使用すると、署名を削除して署名フィールドを保持するか、署名フィールドを削除することで、署名されたPDFテンプレートを再利用できます。
PDFドキュメントにデジタル署名を追加するには、以下の手順に従います。
SignaturePropertiesクラスを使用して、デジタル署名用の証明書を設定します。
署名を保持するようにSignatureFieldクラスを初期化します。
Addメソッドを使用して、署名フィールドをPDFドキュメントに追加します。
署名フィールドを署名プロパティに接続します。
GcPdfDocumentクラスのSignメソッドを使用してドキュメントに署名を付加します。 また、ドキュメントを保存します。
public static void CreatePDF(Stream stream)
{
GcPdfDocument doc = new GcPdfDocument();
Page page = doc.NewPage();
TextFormat tf = new TextFormat() { Font = StandardFonts.Times, FontSize = 14 };
page.Graphics.DrawString(
"Hello, World!\r\nSigned below by GcPdfWeb SignDoc sample." +
"\r\n(Note that some browser built-in viewers may not show the signature.)",
tf, new PointF(72, 72));
// テスト用の証明書を初期化します
var pfxPath = Path.Combine("Resources", "Misc", "GcPdfTest.pfx");
X509Certificate2 cert = new X509Certificate2(File.ReadAllBytes(pfxPath), "qq",
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet
| X509KeyStorageFlags.Exportable);
SignatureProperties sp = new SignatureProperties();
sp.Certificate = cert;
sp.Location = "GcPdfWeb Sample Browser";
sp.SignerName = "GcPdfWeb";
// タイムスタンプを追加します
sp.TimeStamp = new TimeStamp("https://freetsa.org/tsr");
// 署名を保持するために署名フィールドを初期化します
SignatureField sf = new SignatureField();
sf.Widget.Rect = new RectangleF(72, 72 * 2, 72 * 4, 36);
sf.Widget.Page = page;
sf.Widget.BackColor = Color.LightSeaGreen;
sf.Widget.TextFormat.Font = StandardFonts.Helvetica;
sf.Widget.ButtonAppearance.Caption = $"Signer: " +
$"{sp.SignerName}\r\nLocation: {sp.Location}";
// ドキュメントに署名フィールドを追加します
doc.AcroForm.Fields.Add(sf);
// 署名フィールドと署名プロパティを接続します
sp.SignatureField = sf;
// ドキュメントに署名を付加して保存します
// メモ
// - 署名と保存は不可分操作であり、2つを分離することはできません
// - Sign()メソッドに渡されるストリームは読み取り可能である必要があります
doc.Sign(sp, stream);
// ストリームを巻き戻して、作成したばかりのドキュメントを
// 別のGcPdfDocumentに読み込み、署名を確認します
stream.Seek(0, SeekOrigin.Begin);
GcPdfDocument doc2 = new GcPdfDocument();
doc2.Load(stream);
SignatureField sf2 = (SignatureField)doc2.AcroForm.Fields[0];
if (!sf2.Value.VerifySignature())
throw new Exception("Failed to verify the signature");
// 完了(生成および署名されたドキュメントはすでに「ストリーム」に保存されています)
}DioDocs for PDFでは、PDFファイルからデジタル署名を簡単に削除できます。 ライブラリを使用すると、署名フィールドから署名を削除して、PDFファイルの内容を再度使用できるようにすることができます。

署名を削除してPDFドキュメントの署名フィールドを保持するには、以下の手順に従います。
GcPdfDocumentクラスのインスタンスを初期化し、PDFファイルをロードします。
ドキュメント内のすべての署名を削除するには、PDFファイル内のすべての署名フィールドをループする再帰メソッドを呼び出し、SignatureFieldクラスのValueプロパティをnullに設定します。
ドキュメントを保存します。
var doc = new GcPdfDocument();
using (var fs = new FileStream( "TimeSheet.pdf", FileMode.Open, FileAccess.Read))
{
doc.Load(fs);
// フィールドは他のフィールドの子になることができるので、ツリー全体を反復処理する
// ために再帰的な方法を使用します
removeSignatures(doc.AcroForm.Fields);
doc.Save("TimeSheet_NoSign.pdf"); //ドキュメントを保存します
void removeSignatures(FieldCollection fields)
{
foreach (var f in fields)
{
if (f is SignatureField sf)
sf.Value = null; //ドキュメントから署名を削除します
removeSignatures(f.Children);
}
}
}SignatureクラスのContentプロパティを使用することで、PDFドキュメントのデジタル署名から署名情報を抽出できます。署名情報は、署名の有効性を検証するために必要な詳細を提供します。署名からIssuer、IssuerName、SerialNumber、Subject、Thumbprint、NotAfter、NotBefore、SignatureAlgorithmなどの、情報フィールドを抽出できます。
デジタル署名から署名情報を抽出するには、以下の手順に従います。
署名付きのPDFキュメントをロードし、AcroFormクラスのFieldsプロパティで署名を取得します。
SignatureクラスのContentプロパティを使用して、署名に関する追加情報を抽出します。
MemoryStream ms = new MemoryStream(File.ReadAllBytes(@"AdobePDFWithEmptySignatureField.pdf"));
GcPdfDocument doc = new GcPdfDocument();
doc.Load(ms);
//証明書を初期化します
X509Certificate2 cert = new X509Certificate2(@"User.pfx", "User12");
SignatureProperties sp = new SignatureProperties();
sp.SignatureFormat = SignatureFormat.PKCS7Detached;
sp.SignatureDigestAlgorithm = SignatureDigestAlgorithm.SHA1;
sp.Certificate = cert;
sp.Location = "MACHINE";
sp.SignerName = "USER";
sp.SigningDateTime = null;
sp.SignatureField = doc.AcroForm.Fields["EmptySignatureField"];
using (MemoryStream ms2 = new MemoryStream())
{
//ドキュメントに署名します
doc.Sign(sp, ms2, false);
ms2.Seek(0, SeekOrigin.Begin);
//署名されたドキュメントをロードします
GcPdfDocument doc2 = new GcPdfDocument();
doc2.Load(ms2);
//署名フィールドと署名を取得します
SignatureField sf2 = (SignatureField)doc2.AcroForm.Fields["EmptySignatureField"];
var sk = sf2.Value.Content;
//証明書を取得し、そのプロパティを印刷します
var sc = sk.SigningCertificate;
Console.WriteLine($"Subject: {sc.Subject}");
Console.WriteLine($"Issuer: {sc.Issuer}");
Console.WriteLine($"GetEffectiveDateString: {sc.GetEffectiveDateString()}");
Console.WriteLine($"GetExpirationDateString: {sc.GetExpirationDateString()}");
}DioDocs for PDFには、デジタル署名のカスタム実装を実現するように使用できるISignatureBuilderおよびIPkcs7SignatureGeneratorインターフェイスが提供されます。 Pkcs7SignatureBuilderクラスは、ISignatureBuilderインターフェイスを実装し、次のようなさまざまなメソッドとプロパティを提供します。
Pkcs7SignatureBuilder.Formatプロパティ- adbe_pkcs7_detached、ETSI_CAdES_detached、adbe_pkcs7_sha1などのSignatureFormat列挙値に設定して、それぞれの署名を作成できます。
Pkcs7SignatureBuilder.Crlsプロパティ- 証明書失効リストを署名に埋め込むことができます。
Pkcs7SignatureBuilder.IncludeOcspプロパティ- OCPS情報を署名に埋め込むことができます。
Pkcs7SignatureBuilder.CertificateChainプロパティ- 証明書の完全なチェーンを署名に埋め込むことができます。
カスタム署名の実装のいくつかを以下に説明します。
.p12ファイルの証明書を使用してドキュメントに署名するには、以下の手順に従います。
SignaturePropertiesクラスをインスタンス化し、そのオブジェクトを使用してPkcs7SignatureBuilderクラスを初期化します。
.p12ファイル名とそのパスワードをSecurityUtilsクラスのGetCertificateChainメソッドに渡して、証明書のチェーンを構築します。
AcroFormクラスのFieldsプロパティを使用して、ドキュメントに署名フィールドを追加します。
GcPdfDocumentクラスのSignメソッドを使用して、PDFドキュメントに署名して保存します。
using (FileStream fs = new FileStream(@"AdobePDFWithEmptySignatureField.pdf", FileMode.Open))
{
GcPdfDocument doc = new GcPdfDocument();
doc.Load(fs);
SignatureProperties sp = new SignatureProperties();
sp.SignatureBuilder = new Pkcs7SignatureBuilder()
{
CertificateChain = SecurityUtils.GetCertificateChain("1571753451.p12", "test"),
};
sp.SignatureField = doc.AcroForm.Fields[0];
doc.Sign(sp, "signed.pdf");
}有効な証明書を持つUSBトークンを使用してドキュメントに署名できます。詳細については、DioDocs for PDF サンプルブラウザを参照してください。
Azure Key Vaultに保存されている証明書を使用してドキュメントに署名できます。詳細については、DioDocs for PDF サンプルブラウザを参照してください。
カスタム タイムスタンプ トークンを作成してドキュメントに署名するには、ITimeStampGenerator インターフェイスを実装し、SignatureProperties?クラスと TimeStampProperties?クラスの TimeStamp プロパティに割り当てます。ITimeStampGenerator インターフェイスは、タイムスタンプトークンを生成する方法を定義します。
次のサンプルコードは、PDF ドキュメントにカスタム タイムスタンプ トークンとカスタム タイムスタンプ トークンを使用した署名を追加する方法を示しています。
// カスタムのタイムスタンプ ジェネレータを作成します。
public class TimeStampGenerator : ITimeStampGenerator
{
public string ServerUrl;
public string UserName;
public string Password;
public OID HashAlgorithm;
public TimeStampGenerator()
{
HashAlgorithm = new OID("2.16.840.1.101.3.4.2.1", "SHA256");
}
public TimeStampGenerator(string serverUrl, string userName, string password, OID hashAlgorithm)
{
ServerUrl = serverUrl;
UserName = userName;
Password = password;
HashAlgorithm = hashAlgorithm;
}
private static long CopyStream(Stream src, Stream dst, bool useSingleWriteOperation = false)
{
byte[] buffer = new byte[16 * 1024];
int bytesRead;
long result = 0;
while ((bytesRead = src.Read(buffer, 0, buffer.Length)) != 0)
{
dst.Write(buffer, 0, bytesRead);
result += bytesRead;
}
return result;
}
private static void Update(IDigest dgst, byte[] input)
{
Update(dgst, input, 0, input.Length);
}
private static void Update(IDigest dgst, byte[] input, int offset, int len)
{
dgst.BlockUpdate(input, offset, len);
}
private static byte[] Digest(IDigest dgst)
{
byte[] output = new byte[dgst.GetDigestSize()];
dgst.DoFinal(output, 0);
return output;
}
private static byte[] Digest(IDigest dgst, byte[] input)
{
Update(dgst, input);
return Digest(dgst);
}
private static byte[] Digest(IDigest dgst, Stream data)
{
byte[] buf = new byte[8192];
int n;
while ((n = data.Read(buf, 0, buf.Length)) > 0)
{
Update(dgst, buf, 0, n);
}
return Digest(dgst);
}
private static IDigest GetMessageDigest(OID hashAlgorithm)
{
if (hashAlgorithm == OID.HashAlgorithms.MD2)
return new MD2Digest();
else if (hashAlgorithm == OID.HashAlgorithms.MD5)
return new MD5Digest();
else if (hashAlgorithm == OID.HashAlgorithms.SHA1)
return new Sha1Digest();
else if (hashAlgorithm == OID.HashAlgorithms.SHA224)
return new Sha224Digest();
else if (hashAlgorithm == OID.HashAlgorithms.SHA256)
return new Sha256Digest();
else if (hashAlgorithm == OID.HashAlgorithms.SHA384)
return new Sha384Digest();
else if (hashAlgorithm == OID.HashAlgorithms.SHA512)
return new Sha512Digest();
else if (hashAlgorithm == OID.HashAlgorithms.RIPEMD128)
return new RipeMD128Digest();
else if (hashAlgorithm == OID.HashAlgorithms.RIPEMD160)
return new RipeMD160Digest();
else if (hashAlgorithm == OID.HashAlgorithms.RIPEMD256)
return new RipeMD256Digest();
else if (hashAlgorithm == OID.HashAlgorithms.GOST3411)
return new Gost3411Digest();
throw new ArgumentException();
}
private byte[] GetTsaResponseForUserRequest(byte[] requestBytes)
{
HttpWebRequest con;
try
{
con = (HttpWebRequest)WebRequest.Create(ServerUrl);
}
catch (Exception e)
{
throw new Exception(string.Format("Failed to get response from TSA server {0}.", ServerUrl), e);
}
con.ContentLength = requestBytes.Length;
con.ContentType = "application/timestamp-query";
con.Method = "POST";
if (!string.IsNullOrEmpty(UserName))
{
string authInfo = UserName + ":" + Password;
authInfo = Convert.ToBase64String(Encoding.UTF8.GetBytes(authInfo));
con.Headers["Authorization"] = "Basic " + authInfo;
}
using (Stream outp = con.GetRequestStream())
outp.Write(requestBytes, 0, requestBytes.Length);
HttpWebResponse httpWebResponse = (HttpWebResponse)con.GetResponse();
using (Stream stream = httpWebResponse.GetResponseStream())
{
if (stream == null)
return null;
using (MemoryStream ms = new MemoryStream())
{
CopyStream(stream, ms);
string encoding = httpWebResponse.Headers[HttpResponseHeader.ContentEncoding];
byte[] data = ms.ToArray();
if (string.Compare(encoding, "base64", StringComparison.InvariantCultureIgnoreCase) == 0)
data = Convert.FromBase64String(Encoding.ASCII.GetString(data));
return data;
}
}
}
// ハッシュのタイムスタンプトークンを取得します。
private byte[] GetTimeStampTokenForHash(byte[] hash)
{
// タイムスタンプリクエストを設定します。
TimeStampRequestGenerator tsqGenerator = new TimeStampRequestGenerator();
tsqGenerator.SetCertReq(true);
// 乱数を生成します。
BigInteger nonce = BigInteger.ValueOf(unchecked((int)DateTime.Now.Ticks) + Environment.TickCount);
TimeStampRequest request = tsqGenerator.Generate(new DerObjectIdentifier(HashAlgorithm.ID), hash, nonce);
// 通信レイヤーを呼び出します。
byte[] requestBytes = request.GetEncoded();
byte[] respBytes = GetTsaResponseForUserRequest(requestBytes);
// TSA 応答を処理します。
TimeStampResponse response = new TimeStampResponse(respBytes);
// 通信レベル属性(RFC 3161 PKIStatus)を検証します。
response.Validate(request);
PkiFailureInfo failure = response.GetFailInfo();
int value = failure == null ? 0 : failure.IntValue;
if (value != 0)
throw new Exception(string.Format("Invalid TSA response from {0}: {1}.", ServerUrl, response.GetStatusString()));
// タイムスタンプトークンのみ(通信ステータス情報は削除します)を抽出します。
TimeStampToken tsToken = response.TimeStampToken;
if (tsToken == null)
throw new Exception(string.Format("No timetoken in TSA response from {0}.", ServerUrl));
return tsToken.GetEncoded();
}
// タイムスタンプトークンを取得します。
public byte[] GetTimeStampToken(byte[] data)
{
// データのハッシュを構築します。
byte[] hash = Digest(GetMessageDigest(HashAlgorithm), data);
return GetTimeStampTokenForHash(hash);
}
public byte[] GetTimeStampToken(Stream stream)
{
// データのハッシュを構築します。
byte[] hash = Digest(GetMessageDigest(HashAlgorithm), stream);
return GetTimeStampTokenForHash(hash);
}
}
internal class Program
{
static void Main(string[] args)
{
// タイムスタンプ トークンを使用して署名を生成します。
X509Certificate2 crt = new X509Certificate2(@"..\..\..\User.pfx", "User12");
using (FileStream fs = new FileStream(@"..\..\..\AdobePDFWithEmptySignatureField.pdf", FileMode.Open))
{
// GcPdfDocument を初期化します。
GcPdfDocument doc = new GcPdfDocument();
// ストリームから PDF ドキュメントを読み込みます。
doc.Load(fs);
// SignaturePropertiesを初期化します。
SignatureProperties sp = new SignatureProperties();
// 証明書のチェーンを構築します。
sp.SignatureBuilder = new Pkcs7SignatureBuilder()
{
CertificateChain = new X509Certificate2[] { crt },
};
// カスタム タイムスタンプを追加します。
sp.TimeStamp = new TimeStampGenerator()
{
ServerUrl = @"http://ts.ssl.com",
};
// 署名フィールドに署名を追加します。
sp.SignatureField = doc.AcroForm.Fields[0];
// PDF ドキュメントに署名します。
doc.Sign(sp, "signed.pdf");
}
// タイムスタンプ付きのドキュメントを生成します。
using (FileStream fs = new FileStream(@"..\..\..\AdobePDFWithEmptySignatureField.pdf", FileMode.Open))
{
// GcPdfDocument を初期化します。
GcPdfDocument doc = new GcPdfDocument();
// ストリームから PDF ドキュメントを読み込みます。
doc.Load(fs);
// TimeStampPropertiesを初期化します。
TimeStampProperties tsp = new TimeStampProperties();
// カスタムタイムスタンプを追加します。
tsp.TimeStamp = new TimeStampGenerator()
{
ServerUrl = @"http://ts.ssl.com",
};
// 署名フィールドにタイムスタンプを追加します。
tsp.SignatureField = doc.AcroForm.Fields[0];
// タイムスタンプを追加してドキュメントを保存します。
doc.TimeStamp(tsp, "timestamp.pdf");
}
}
}

DioDocs for PDFでは、PDF Advanced Electronic Signatures(PAdES)を使用してPDFドキュメントにデジタル署名を付加できます。 PAdESは、PDFドキュメントが電子的に署名されるときに使用される拡張機能と制限のグループを参照する一連の規格です。 PAdES形式を使用して署名されたドキュメントは、長期間有効です。
PAdESでは、DioDocs for PDFは次のレベルのデジタル署名の検証をサポートしています。
Bレベル:日付で有効な署名証明書を使用して電子署名が実行されたことを示します。
Tレベル:Bレベルと同様に、署名が特定の日時に存在したことを証明するために、タイムスタンプを追加するだけです。
LTレベル:検証関連の情報を更にDocuments Security Store(DSS)に追加します。DioDocs for PDFでは、DSSがDocumentSecurityStoreクラスによって表されます。
LTAレベル:検証関連情報に加えて、DSSにもタイムスタンプのトークンを追加する必要があります。これにより、指定された時間に検証データが存在したという証拠が作成されます。
DioDocs for PDFでは、以下で説明するように、SignaturePropertiesクラスのCreatePAdES_B_BメソッドとCreatePAdES_B_Tメソッドを使用して、PDFドキュメントにそれぞれの署名を作成できます。さらに、GrapeCity.Documents.Pdf.Security.DocumentSecurityStoreクラスとGcPdfDocument.TimeStamp()メソッドを使用して、B-LTやB-LTAレベルなどの高度な電子署名を作成できます。
PAdES B-B署名を作成するには、以下の手順に従います。
X509Certificate2クラスを使用して証明書を初期化し、証明書のファイル名とパスワードを渡して証明書にアクセスします。
証明書インスタンスをSignaturePropertiesクラスのCreatePAdES_B_Bメソッドに渡して、PAdESB-B署名を作成します。
最初のAcroFormフィールドを設定して、SignatureFieldプロパティを使用して署名を保存します。
GcPdfDocumentクラスのSignメソッドを使用して、PDFドキュメントに署名を付加して保存します。
using (FileStream fs = new FileStream(@"PDFWithEmptySignatureField.pdf", FileMode.Open))
{
GcPdfDocument doc = new GcPdfDocument();
doc.Load(fs);
X509Certificate2 cert = new X509Certificate2("User.pfx", "User12");
SignatureProperties sp = SignatureProperties.CreatePAdES_B_B(cert);
sp.SignatureAppearance.Caption = "PAdES B-B";
sp.SignatureField = doc.AcroForm.Fields[0];
doc.Sign(sp, "SignPAdESBB.pdf");
}PAdES B-T署名を作成するには、以下の手順に従います。
X509Certificate2クラスを使用して証明書を初期化し、証明書ファイル名とパスワードを渡して証明書にアクセスします。
タイムスタンプと証明書インスタンスをSignaturePropertiesクラスのCreatePAdES_B_Tメソッドに渡して、PAdESB-T署名を作成します。
最初のAcroFormフィールドを設定して、SignatureFieldプロパティを使用して署名を保存します。
GcPdfDocumentクラスのSignメソッドを使用して、PDFドキュメントに署名を付加して保存します。
using (FileStream fs = new FileStream(@"PDFWithEmptySignatureField.pdf", FileMode.Open))
{
GcPdfDocument doc = new GcPdfDocument();
doc.Load(fs);
X509Certificate2 cert = new X509Certificate2("User.pfx", "User12");
SignatureProperties sp = SignatureProperties.CreatePAdES_B_T(new TimeStamp("https://freetsa.org/tsr"), cert);
sp.SignatureAppearance.Caption = "PAdES B-T";
sp.SignatureField = doc.AcroForm.Fields[0];
doc.Sign(sp, "SignPAdESBT.pdf");
}B-LT署名は、署名の長期間有効に必要なすべてのプロパティを追加することで、B-T署名に基づいて構築されています。 PAdES B-LT署名を作成するには、以下の手順に従います。
PAdES B-T署名を付加して保存したPDFドキュメントを読み込みます。
DocumentSecurityStoreクラスのAddVerificationメソッドを使用して、署名にLTV情報を追加します。
Saveメソッドを使用して、増分更新モードでドキュメントを保存します。
using (FileStream fs = new FileStream(@"SignPAdESBT.pdf", FileMode.Open))
{
GcPdfDocument doc = new GcPdfDocument();
doc.Load(fs);
// PAdES B-LTに準拠した署名にするためのLTV情報を追加します
SignatureField signField = (SignatureField)doc.AcroForm.Fields[0];
var sig = signField.Value;
var vp = new DocumentSecurityStore.VerificationParams();
X509Certificate2 cert = new X509Certificate2("User.pfx", "User12");
vp.Certificates = new X509Certificate2[] { cert
};
if (!doc.SecurityStore.AddVerification(sig, vp))
throw new Exception($"{sig.Name}に対する検証を追加できませんでした。");
doc.Save("SignPAdESBLT.pdf", SaveMode.IncrementalUpdate);
}B-LTA署名は、有効資料にタイムスタンプのトークンを追加することで、B-LT署名に基づいて構築されています。 PAdES B-LTA署名を作成するには、次の手順に従います。
PAdES B-LT署名を付加して保存したPDFドキュメントを読み込みます。
TimeStampPropertiesクラスを使用してPDFにタイムスタンプを追加し、PAdESB-LTAレベルに準拠させます。
TimeStamp()メソッドを呼び出し、タイムスタンププロパティを含んだドキュメントを保存します。
using (FileStream fs = new FileStream(@"SignPAdESBLT.pdf", FileMode.Open))
{
GcPdfDocument doc = new GcPdfDocument();
doc.Load(fs);
// 署名されたPDFにタイムスタンプを追加し、B-LTAレベルに準拠したドキュメントにします
TimeStampProperties ts = new TimeStampProperties()
{
TimeStamp = new TimeStamp(@"http://ts.ssl.com"),
};
// PDFにタイムスタンプを追加して保存します
doc.TimeStamp(ts, "SignPAdESBLTA.pdf");
}