SignAzureKeyVault.cs
- //
- // This code is part of Document Solutions for PDF demos.
- // Copyright (c) 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
- {
- // This sample shows how to sign an existing PDF file that contains
- // an empty signature field with a certificate that is stored
- // in an Azure Key Vault.
- //
- // The sample includes a ready to use utility class AzureSignatureGenerator
- // that implements the GrapeCity.Documents.Pdf.IPkcs7SignatureGenerator interface,
- // and can be used to sign PDFs with certificates stored in Azure Key Vault.
- //
- // Please note that when run directly off the DsPdf demo site,
- // this sample will NOT sign the PDF, as it passes dummy Azure credentials
- // to the AzureSignatureGenerator's ctor. You will need to download the sample
- // and provide your own credentials for the sample code to actually sign a 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
- {
- // This WILL NOT WORK due to dummy Azure credentials.
- // Supply valid credentials to actually sign the 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(
- "Signing failed because dummy Azure credentials were used.\n" +
- "Use valid Azure Key Vault credentials to sign the PDF.",
- page,
- new RectangleF(r.Left, r.Bottom + 24, page.Size.Width - r.Left * 2, 0));
- doc.Save(stream);
- }
-
- // Done.
- return doc.Pages.Count;
- }
- }
-
- /// <summary>
- /// Implements <see cref="IPkcs7SignatureGenerator"/>
- /// and allows generating a digital signature using
- /// a certificate stored in Azure Key Vault.
- /// </summary>
- public class AzureSignatureGenerator : IPkcs7SignatureGenerator, IDisposable
- {
- private CertificateClient _certificateClient;
- private X509Certificate2 _certificate;
- private CryptographyClient _cryptographyClient;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="AzureSignatureGenerator"/> class.
- /// </summary>
- /// <param name="keyVaultName">
- /// The name of the Key Vault storage used to create a URL in the form
- /// <b>https://{keyVaultName}.vault.azure.net/</b> that will be passed to
- /// the <see cref="CertificateClient"/> ctor.</param>
- /// <param name="tenantId">
- /// The Azure Active Directory tenant (directory) ID of the service principal.
- /// This value will be used to create the <see cref="ClientSecretCredential"/>.</param>
- /// <param name="clientId">
- /// The client (application) ID of the service principal.
- /// This value will be used to create the <see cref="ClientSecretCredential"/>.</param>
- /// <param name="clientSecret">
- /// The client secret that was generated for the App Registration used to authenticate the client.
- /// This value will be used to create the <see cref="ClientSecretCredential"/>.</param>
- /// <param name="certificateName">
- /// The name of the certificate to be used for the signature.</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>
- /// Gets the ID of the hash algorithm.
- /// </summary>
- public OID HashAlgorithm => OID.HashAlgorithms.SHA256;
-
- /// <summary>
- /// Gets the ID of the encryption algorithm.
- /// </summary>
- public OID DigestEncryptionAlgorithm => OID.EncryptionAlgorithms.RSA;
-
- /// <summary>
- /// Gets the certificate.
- /// </summary>
- public X509Certificate2 Certificate => _certificate;
-
- /// <summary>
- /// Signs data.
- /// </summary>
- /// <param name="input">The input data to sign.</param>
- /// <returns>The signed data.</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>
- /// Releases resources used by this object.
- /// </summary>
- public void Dispose()
- {
- _certificateClient = null;
- if (_certificate != null)
- {
- _certificate.Dispose();
- _certificate = null;
- }
- _cryptographyClient = null;
- }
- }
- }
-