''
'' This code is part of Document Solutions for PDF .NET demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System
Imports System.IO
Imports System.Drawing
Imports System.Security.Cryptography.X509Certificates
Imports Org.BouncyCastle.Asn1
Imports Org.BouncyCastle.Crypto
Imports Org.BouncyCastle.Security
Imports Org.BouncyCastle.Pkcs
Imports Org.BouncyCastle.Crypto.Parameters
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Pdf.Security
Imports GrapeCity.Documents.Pdf.AcroForms
Imports GrapeCity.Documents.Text
'' 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 Function
End 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 Function
End Class