SignWithECDSA.cs
  1. //
  2. // This code is part of Document Solutions for PDF demos.
  3. // Copyright (c) MESCIUS inc. All rights reserved.
  4. //
  5. using System;
  6. using System.IO;
  7. using System.Drawing;
  8. using System.Security.Cryptography;
  9. using System.Security.Cryptography.X509Certificates;
  10.  
  11. using Org.BouncyCastle.Asn1;
  12. using Org.BouncyCastle.Crypto;
  13. using Org.BouncyCastle.Security;
  14. using Org.BouncyCastle.Pkcs;
  15. using Org.BouncyCastle.Crypto.Parameters;
  16.  
  17. using GrapeCity.Documents.Pdf;
  18. using GrapeCity.Documents.Pdf.Security;
  19. using GrapeCity.Documents.Pdf.AcroForms;
  20. using GrapeCity.Documents.Text;
  21.  
  22.  
  23. namespace DsPdfWeb.Demos
  24. {
  25. // This sample shows how to sign an existing PDF file that contains
  26. // an empty signature field with an Elliptic Curve Digital Signature Algorithm
  27. // (ECDSA) certificate.
  28. //
  29. // The sample includes a ready to use class BCSignatureGenerator that implements
  30. // the GrapeCity.Documents.Pdf.IPkcs7SignatureGenerator interface using the
  31. // BouncyCastle.Cryptography package. Because unlike the current .NET system libraries,
  32. // BouncyCastle supports ECDSA, the BCSignatureGenerator class can be used in your
  33. // applications to handle ECDSA certificates.
  34. public class SignWithECDSA
  35. {
  36. public int CreatePDF(Stream stream)
  37. {
  38. var certPath = Path.Combine("Resources", "Misc", "DsPdfTest3_ECDSA.pfx");
  39. var certPwd = "password";
  40.  
  41. var doc = new GcPdfDocument();
  42. using var s = File.OpenRead(Path.Combine("Resources", "PDFs", "SignWithECDSA.pdf"));
  43. doc.Load(s);
  44.  
  45. var sp = new SignatureProperties()
  46. {
  47. SignatureBuilder = new Pkcs7SignatureBuilder()
  48. {
  49. SignatureGenerator = new BCSignatureGenerator(certPath, certPwd, OID.HashAlgorithms.SHA256),
  50. CertificateChain = SecurityUtils.GetCertificateChain(certPath, certPwd),
  51. },
  52. SignatureField = doc.AcroForm.Fields[0]
  53. };
  54. sp.SignatureAppearance.Caption = "ECDSA";
  55. doc.Sign(sp, stream);
  56.  
  57. // Done.
  58. return doc.Pages.Count;
  59. }
  60. }
  61.  
  62. /// <summary>
  63. /// Implements <see cref="IPkcs7SignatureGenerator"/>.
  64. /// This implementation uses BouncyCastle libraries which,
  65. /// unlike the current .NET system libraries, support ECDSA
  66. /// (Elliptic Curve Digital Signature Algorithm) keys.
  67. /// </summary>
  68. public class BCSignatureGenerator : IPkcs7SignatureGenerator
  69. {
  70. private OID _hashAlgorithm;
  71. private OID _encryptionAlgorithm;
  72. private string _encryptionAlgorithmName;
  73. private ICipherParameters _key;
  74.  
  75. public BCSignatureGenerator(ICipherParameters key, OID hashAlgorithm)
  76. {
  77. _hashAlgorithm = hashAlgorithm;
  78. if (key is RsaKeyParameters)
  79. {
  80. _encryptionAlgorithm = OID.EncryptionAlgorithms.RSA;
  81. _encryptionAlgorithmName = "RSA";
  82. }
  83. else if (key is DsaKeyParameters)
  84. {
  85. _encryptionAlgorithm = OID.EncryptionAlgorithms.DSA;
  86. _encryptionAlgorithmName = "DSA";
  87. }
  88. else if (key is ECKeyParameters)
  89. {
  90. _encryptionAlgorithm = OID.EncryptionAlgorithms.ECDSA;
  91. _encryptionAlgorithmName = "ECDSA";
  92. }
  93. else
  94. {
  95. throw new Exception($"Unknown algorithm used in the private key [{key}]");
  96. }
  97. _key = key;
  98. }
  99.  
  100. public BCSignatureGenerator(byte[] certificateData, string password, OID hashAlgorithm)
  101. : this(GetPrivateKey(certificateData, password), hashAlgorithm)
  102. {
  103. }
  104.  
  105. public BCSignatureGenerator(Stream certificateStream, string password, OID hashAlgorithm)
  106. : this(GetPrivateKey(certificateStream, password), hashAlgorithm)
  107. {
  108. }
  109.  
  110. public BCSignatureGenerator(string certificateFileName, string password, OID hashAlgorithm)
  111. : this(GetPrivateKey(certificateFileName, password), hashAlgorithm)
  112. {
  113. }
  114.  
  115. public OID HashAlgorithm => _hashAlgorithm;
  116.  
  117. public OID DigestEncryptionAlgorithm => _encryptionAlgorithm;
  118.  
  119. public byte[] SignData(byte[] digest)
  120. {
  121. string han = DigestUtilities.GetAlgorithmName(new DerObjectIdentifier(_hashAlgorithm.ToString()));
  122. string algorithm = han + "with" + _encryptionAlgorithmName;
  123. ISigner sig = SignerUtilities.GetSigner(algorithm);
  124. sig.Init(true, _key);
  125. sig.BlockUpdate(digest, 0, digest.Length);
  126. return sig.GenerateSignature();
  127. }
  128.  
  129. public static ICipherParameters GetPrivateKey(Stream certificateStream, string password)
  130. {
  131. char[] p = new char[password.Length];
  132. for (int i = 0; i < p.Length; i++)
  133. p[i] = password[i];
  134. Pkcs12Store pk12 = new Pkcs12StoreBuilder().Build();
  135. pk12.Load(certificateStream, p);
  136. foreach (var a in pk12.Aliases)
  137. {
  138. string alias = (string)a;
  139. if (pk12.IsKeyEntry(alias))
  140. {
  141. var key = pk12.GetKey(alias);
  142. if (key.Key.IsPrivate)
  143. return key.Key;
  144. }
  145. }
  146. throw new Exception("Cannot get private key.");
  147. }
  148.  
  149. public static ICipherParameters GetPrivateKey(string certificateFilePath, string password)
  150. {
  151. using var fs = new FileStream(certificateFilePath, FileMode.Open);
  152. return GetPrivateKey(fs, password);
  153. }
  154.  
  155. public static ICipherParameters GetPrivateKey(byte[] certificateData, string password)
  156. {
  157. using var ms = new MemoryStream(certificateData);
  158. return GetPrivateKey(ms, password);
  159. }
  160. }
  161. }
  162.