//
// 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.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Crypto.Parameters;
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 an Elliptic Curve Digital Signature Algorithm
// (ECDSA) certificate.
//
// The sample includes a ready to use class BCSignatureGenerator that implements
// the GrapeCity.Documents.Pdf.IPkcs7SignatureGenerator interface using the
// BouncyCastle.Cryptography package. Because unlike the current .NET system libraries,
// BouncyCastle supports ECDSA, the BCSignatureGenerator class can be used in your
// applications to handle ECDSA certificates.
public class SignWithECDSA
{
public int CreatePDF(Stream stream)
{
var certPath = Path.Combine("Resources", "Misc", "DsPdfTest3_ECDSA.pfx");
var certPwd = "password";
var doc = new GcPdfDocument();
using var s = File.OpenRead(Path.Combine("Resources", "PDFs", "SignWithECDSA.pdf"));
doc.Load(s);
var sp = new SignatureProperties()
{
SignatureBuilder = new Pkcs7SignatureBuilder()
{
SignatureGenerator = new BCSignatureGenerator(certPath, certPwd, OID.HashAlgorithms.SHA256),
CertificateChain = SecurityUtils.GetCertificateChain(certPath, certPwd),
},
SignatureField = doc.AcroForm.Fields[0]
};
sp.SignatureAppearance.Caption = "ECDSA";
doc.Sign(sp, stream);
// Done.
return doc.Pages.Count;
}
}
/// <summary>
/// Implements <see cref="IPkcs7SignatureGenerator"/>.
/// This implementation uses BouncyCastle libraries which,
/// unlike the current .NET system libraries, support ECDSA
/// (Elliptic Curve Digital Signature Algorithm) keys.
/// </summary>
public class BCSignatureGenerator : IPkcs7SignatureGenerator
{
private OID _hashAlgorithm;
private OID _encryptionAlgorithm;
private string _encryptionAlgorithmName;
private ICipherParameters _key;
public BCSignatureGenerator(ICipherParameters key, OID hashAlgorithm)
{
_hashAlgorithm = hashAlgorithm;
if (key is RsaKeyParameters)
{
_encryptionAlgorithm = OID.EncryptionAlgorithms.RSA;
_encryptionAlgorithmName = "RSA";
}
else if (key is DsaKeyParameters)
{
_encryptionAlgorithm = OID.EncryptionAlgorithms.DSA;
_encryptionAlgorithmName = "DSA";
}
else if (key is ECKeyParameters)
{
_encryptionAlgorithm = OID.EncryptionAlgorithms.ECDSA;
_encryptionAlgorithmName = "ECDSA";
}
else
{
throw new Exception($"Unknown algorithm used in the private key [{key}]");
}
_key = key;
}
public BCSignatureGenerator(byte[] certificateData, string password, OID hashAlgorithm)
: this(GetPrivateKey(certificateData, password), hashAlgorithm)
{
}
public BCSignatureGenerator(Stream certificateStream, string password, OID hashAlgorithm)
: this(GetPrivateKey(certificateStream, password), hashAlgorithm)
{
}
public BCSignatureGenerator(string certificateFileName, string password, OID hashAlgorithm)
: this(GetPrivateKey(certificateFileName, password), hashAlgorithm)
{
}
public OID HashAlgorithm => _hashAlgorithm;
public OID DigestEncryptionAlgorithm => _encryptionAlgorithm;
public byte[] SignData(byte[] digest)
{
string han = DigestUtilities.GetAlgorithmName(new DerObjectIdentifier(_hashAlgorithm.ToString()));
string algorithm = han + "with" + _encryptionAlgorithmName;
ISigner sig = SignerUtilities.GetSigner(algorithm);
sig.Init(true, _key);
sig.BlockUpdate(digest, 0, digest.Length);
return sig.GenerateSignature();
}
public static ICipherParameters GetPrivateKey(Stream certificateStream, string password)
{
char[] p = new char[password.Length];
for (int i = 0; i < p.Length; i++)
p[i] = password[i];
Pkcs12Store pk12 = new Pkcs12StoreBuilder().Build();
pk12.Load(certificateStream, p);
foreach (var a in pk12.Aliases)
{
string alias = (string)a;
if (pk12.IsKeyEntry(alias))
{
var key = pk12.GetKey(alias);
if (key.Key.IsPrivate)
return key.Key;
}
}
throw new Exception("Cannot get private key.");
}
public static ICipherParameters GetPrivateKey(string certificateFilePath, string password)
{
using var fs = new FileStream(certificateFilePath, FileMode.Open);
return GetPrivateKey(fs, password);
}
public static ICipherParameters GetPrivateKey(byte[] certificateData, string password)
{
using var ms = new MemoryStream(certificateData);
return GetPrivateKey(ms, password);
}
}
}