SignWithECDSA.vb
'''' This code is part of Document Solutions for PDF .NET demos.'' Copyright (c) MESCIUS inc. All rights reserved.''Imports SystemImports System.IOImports Org.BouncyCastle.Asn1Imports Org.BouncyCastle.CryptoImports Org.BouncyCastle.SecurityImports Org.BouncyCastle.PkcsImports Org.BouncyCastle.Crypto.ParametersImports GrapeCity.Documents.PdfImports GrapeCity.Documents.Pdf.Security
'' 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 Function CreatePDF(ByVal stream As Stream) As Integer Dim certPath = Path.Combine("Resources", "Misc", "DsPdfTest3_ECDSA.pfx") Dim certPwd = "password"
Dim doc = New GcPdfDocument() Using s = File.OpenRead(Path.Combine("Resources", "PDFs", "SignWithECDSA.pdf")) doc.Load(s)
Dim sp = New SignatureProperties() With { .SignatureBuilder = New Pkcs7SignatureBuilder() With { .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) End Using
'' Done. Return doc.Pages.Count End FunctionEnd Class
''' <summary>''' Implements 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 Implements IPkcs7SignatureGenerator
Private _hashAlgorithm As OID Private _encryptionAlgorithm As OID Private _encryptionAlgorithmName As String Private _key As ICipherParameters
Public Sub New(ByVal key As ICipherParameters, ByVal hashAlgorithm As OID) _hashAlgorithm = hashAlgorithm If TypeOf key Is RsaKeyParameters Then _encryptionAlgorithm = OID.EncryptionAlgorithms.RSA _encryptionAlgorithmName = "RSA" ElseIf TypeOf key Is DsaKeyParameters Then _encryptionAlgorithm = OID.EncryptionAlgorithms.DSA _encryptionAlgorithmName = "DSA" ElseIf TypeOf key Is ECKeyParameters Then _encryptionAlgorithm = OID.EncryptionAlgorithms.ECDSA _encryptionAlgorithmName = "ECDSA" Else Throw New Exception($"Unknown algorithm used in the private key [{key}]") End If _key = key End Sub
Public Sub New(ByVal certificateData As Byte(), ByVal password As String, ByVal hashAlgorithm As OID) Me.New(GetPrivateKey(certificateData, password), hashAlgorithm) End Sub
Public Sub New(ByVal certificateStream As Stream, ByVal password As String, ByVal hashAlgorithm As OID) Me.New(GetPrivateKey(certificateStream, password), hashAlgorithm) End Sub
Public Sub New(ByVal certificateFileName As String, ByVal password As String, ByVal hashAlgorithm As OID) Me.New(GetPrivateKey(certificateFileName, password), hashAlgorithm) End Sub
Public ReadOnly Property HashAlgorithm As OID Implements IPkcs7SignatureGenerator.HashAlgorithm Get Return _hashAlgorithm End Get End Property
Public ReadOnly Property DigestEncryptionAlgorithm As OID Implements IPkcs7SignatureGenerator.DigestEncryptionAlgorithm Get Return _encryptionAlgorithm End Get End Property
Public Function SignData(ByVal digest As Byte()) As Byte() Implements IPkcs7SignatureGenerator.SignData Dim han = DigestUtilities.GetAlgorithmName(New DerObjectIdentifier(_hashAlgorithm.ToString())) Dim algorithm = han & "with" & _encryptionAlgorithmName Dim sig As ISigner = SignerUtilities.GetSigner(algorithm) sig.Init(True, _key) sig.BlockUpdate(digest, 0, digest.Length) Return sig.GenerateSignature() End Function
Public Shared Function GetPrivateKey(ByVal certificateStream As Stream, ByVal password As String) As ICipherParameters Dim p = New Char(password.Length - 1) {} For i = 0 To p.Length - 1 p(i) = password(i) Next Dim pk12 As Pkcs12Store = New Pkcs12StoreBuilder().Build() pk12.Load(certificateStream, p) For Each a In pk12.Aliases Dim aliasStr = DirectCast(a, String) If pk12.IsKeyEntry(aliasStr) Then Dim keyEntry = pk12.GetKey(aliasStr) If keyEntry.Key.IsPrivate Then Return keyEntry.Key End If End If Next Throw New Exception("Cannot get private key.") End Function
Public Shared Function GetPrivateKey(ByVal certificateFilePath As String, ByVal password As String) As ICipherParameters Using fs = New FileStream(certificateFilePath, FileMode.Open) Return GetPrivateKey(fs, password) End Using End Function
Public Shared Function GetPrivateKey(ByVal certificateData As Byte(), ByVal password As String) As ICipherParameters Using ms = New MemoryStream(certificateData) Return GetPrivateKey(ms, password) End Using End FunctionEnd Class