// This code is part of GrapeCity Documents for PDF samples.
// Copyright (c) GrapeCity, 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 GcPdfWeb.Samples
    // 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 GcPdf 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)
            GcPdfDocument doc = new GcPdfDocument();
            using var s = File.OpenRead(Path.Combine("Resources", "PDFs", "SignAzureKeyVault.pdf"));

                // This WILL NOT WORK due to dummy Azure credentials.
                // Supply valid credentials to actually sign the PDF.
                using var sg = new AzureSignatureGenerator(

                SignatureProperties 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;
                    "Signing failed because dummy Azure credentials were used.\n" +
                    "Use valid Azure Key Vault credentials to sign the PDF.",
                    new RectangleF(r.Left, r.Bottom + 24, page.Size.Width - r.Left * 2, 0));

            // 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}</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)
            Uri keyVaultUri = new Uri($"https://{keyVaultName}");
            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.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 = null;
            _cryptographyClient = null;