SignAzureKeyVault.cs
//
// このコードは、DioDocs for PDF のサンプルの一部として提供されています。
// © MESCIUS inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Azure.Core;
using Azure.Identity;
using Azure.Security.KeyVault.Certificates;
using Azure.Security.KeyVault.Keys;
using Azure.Security.KeyVault.Keys.Cryptography;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Pdf.Security;
using GrapeCity.Documents.Pdf.AcroForms;
using GrapeCity.Documents.Text;
namespace DsPdfWeb.Demos
{
// このサンプルでは、空の署名フィールドを含む既存の PDF ファイルに、
// Azure Key Vault に格納された証明書を使用して署名する方法を
// 示しています。
//
// サンプルには、GrapeCity.Documents.Pdf.IPkcs7SignatureGenerator インターフェースを実装した、
// 実用的な AzureSignatureGenerator クラスが含まれており、Azure Key Vault に
// 格納された証明書を使用して PDF に署名するために利用することができます。
//
// なお、デモサイトで実行する場合、このサンプルはダミーの Azure 認証情報を
// AzureSignatureGenerator クラスのコンストラクターに渡すため、PDF には署名しません。
// 実際に PDF に署名するためには、サンプルをダウンロードし、サンプルコードに
// 独自の認証情報を指定する必要があります。
//
public class SignAzureKeyVault
{
public int CreatePDF(Stream stream)
{
var doc = new GcPdfDocument();
using var s = File.OpenRead(Path.Combine("Resources", "PDFs", "SignAzureKeyVault.pdf"));
doc.Load(s);
try
{
// ダミーの Azure 認証情報であるため、署名はしません。
// 実際に PDF に署名するためには、有効な認証情報を指定してください。
using var sg = new AzureSignatureGenerator(
"keyVaultName",
"tenantId",
"clientId",
"clientSecret",
"certificateName");
var sp = new SignatureProperties()
{
SignatureBuilder = new Pkcs7SignatureBuilder()
{
SignatureGenerator = sg,
CertificateChain = new X509Certificate2[] { sg.Certificate },
},
SignatureField = doc.AcroForm.Fields[0]
};
doc.Sign(sp, stream);
}
catch (Exception)
{
var page = doc.Pages[0];
var r = doc.AcroForm.Fields[0].Widgets[0].Rect;
Common.Util.AddNote(
"ダミーの Azure 認証情報が使用されたため、署名に失敗しました。\n" +
"PDF に署名するには、有効な Azure Key Vault の認証情報を指定してください。",
page,
new RectangleF(72, r.Bottom + 24, page.Size.Width - 72 * 2, 100));
doc.Save(stream);
}
// 終了
return doc.Pages.Count;
}
}
/// <summary>
/// <see cref="IPkcs7SignatureGenerator"/> を実装し、
/// Azure Key Vault に格納された証明書を使用して
/// デジタル署名を生成できるようにします。
/// </summary>
public class AzureSignatureGenerator : IPkcs7SignatureGenerator, IDisposable
{
private CertificateClient _certificateClient;
private X509Certificate2 _certificate;
private CryptographyClient _cryptographyClient;
/// <summary>
/// <see cref="AzureSignatureGenerator"/> クラスの新しいインスタンスを初期化します。
/// </summary>
/// <param name="keyVaultName">
/// <see cref="CertificateClient"/> クラスのコンストラクタに渡す
/// <b>https://{keyVaultName}.vault.azure.net/</b> 形式の URL を
/// 作成するために使用される Key Vault ストレージの名前</param>
/// <param name="tenantId">
/// Azure Active Directory のテナント(ディレクトリ)のサービスプリンシパル ID
/// (この値は <see cref="ClientSecretCredential"/> の作成に使用される)</param>
/// <param name="clientId">
/// クライアント(アプリケーション)のサービスプリンシパル ID
/// (この値は <see cref="ClientSecretCredential"/> の作成に使用される)</param>
/// <param name="clientSecret">
/// クライアントの認証に使用される、アプリ登録用に生成されたクライアントシークレット
/// (この値は <see cref="ClientSecretCredential"/> の作成に使用される)</param>
/// <param name="certificateName">
/// 署名に使用する証明書の名前</param>
public AzureSignatureGenerator(
string keyVaultName,
string tenantId,
string clientId,
string clientSecret,
string certificateName)
{
var keyVaultUri = new Uri($"https://{keyVaultName}.vault.azure.net/");
TokenCredential credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
_certificateClient = new CertificateClient(keyVaultUri, credential);
var c = _certificateClient.GetCertificate(certificateName);
_certificate = new X509Certificate2(c.Value.Cer);
_cryptographyClient = new CryptographyClient(c.Value.KeyId, credential);
}
/// <summary>
/// ハッシュアルゴリズムの ID を取得します。
/// </summary>
public OID HashAlgorithm => OID.HashAlgorithms.SHA256;
/// <summary>
/// 暗号化アルゴリズムの ID を取得します。
/// </summary>
public OID DigestEncryptionAlgorithm => OID.EncryptionAlgorithms.RSA;
/// <summary>
/// 証明書を取得します。
/// </summary>
public X509Certificate2 Certificate => _certificate;
/// <summary>
/// 署名データです。
/// </summary>
/// <param name="input">署名する入力データ</param>
/// <returns>署名されたデータ</returns>
public byte[] SignData(byte[] input)
{
var hashDigest = new Sha256Digest();
byte[] hash = new byte[hashDigest.GetDigestSize()];
hashDigest.Reset();
hashDigest.BlockUpdate(input, 0, input.Length);
hashDigest.DoFinal(hash, 0);
byte[] result = _cryptographyClient.Sign(SignatureAlgorithm.RS256, hash).Signature;
return result;
}
/// <summary>
/// オブジェクトが使用しているリソースを解放します。
/// </summary>
public void Dispose()
{
_certificateClient = null;
if (_certificate != null)
{
_certificate.Dispose();
_certificate = null;
}
_cryptographyClient = null;
}
}
}