'''' This code is part of Document Solutions for PDF .NET demos.'' Copyright (c) MESCIUS inc. All rights reserved.''ImportsSystemImportsSystem.IOImportsSystem.DrawingImportsSystem.TextImportsSystem.Collections.GenericImportsSystem.Security.Cryptography.X509CertificatesImportsOrg.BouncyCastle.CryptoImportsOrg.BouncyCastle.Crypto.DigestsImportsOrg.BouncyCastle.Asn1ImportsOrg.BouncyCastle.Asn1.X509ImportsNet.Pkcs11Interop.CommonImportsNet.Pkcs11Interop.HighLevelAPIImportsGrapeCity.Documents.PdfImportsGrapeCity.Documents.Pdf.Security '' This sample shows how to sign an existing PDF file that contains'' an empty signature field with a certificate that is stored'' on a USB Token for DSC (Digital Signature Certificate).'''' The sample includes a ready to use utility class Pkcs11SignatureGenerator'' that implements the GrapeCity.Documents.Pdf.IPkcs7SignatureGenerator interface,'' and can be used to sign PDFs with certificates stored on a USB Token for DSC.'''' Please note that when run directly off the DsPdf demo site,'' this sample will NOT sign the PDF, as it passes dummy library name/parameters.'' to the Pkcs11SignatureGenerator's ctor. You will need to download the sample'' and provide your own library and parameters for the sample code to actually sign a PDF.PublicClassSignUsbTokenFunctionCreatePDF(ByVal stream AsStream) AsIntegerDim doc = NewGcPdfDocument()Using s = File.OpenRead(Path.Combine("Resources", "PDFs", "SignUsbToken.pdf")) doc.Load(s) Try'' This WILL NOT WORK due to dummy USB Token for DSC library name/parameters.'' Supply valid library name and parameters to actually sign the PDF.Using sg = NewPkcs11SignatureGenerator("path-to-dummy-PKCS11.dll",Nothing,Nothing,Encoding.ASCII.GetBytes("12345"),Nothing,Nothing,OID.HashAlgorithms.SHA512) Dim sp = NewSignatureProperties() With { .SignatureBuilder = NewPkcs7SignatureBuilder() With { .SignatureGenerator = sg, .CertificateChain = NewX509Certificate2() {sg.Certificate} }, .SignatureField = doc.AcroForm.Fields(0) } doc.Sign(sp, stream)EndUsingCatch e AsExceptionDim page = doc.Pages(0)Dim r = doc.AcroForm.Fields(0).Widgets(0).RectUtil.AddNote("Signing failed because a dummy USB Token for DSC library name and dummy parameters were used." & vbLf &"Provide a valid USB Token library and correct parameters to sign the PDF.", page,NewRectangleF(r.Left, r.Bottom + 24, page.Size.Width - r.Left * 2, 0)) doc.Save(stream)EndTryEndUsing '' Done.Return doc.Pages.CountEndFunctionEndClass ''' <summary>''' Implements IPkcs7SignatureGenerator''' and allows generating a digital signature using a certificate''' stored on a USB Token for DSC (Digital Signature Certificate).'''''' The Pkcs11Interop NuGet package is used to manage the token.''' </summary>PublicClassPkcs11SignatureGeneratorImplementsIPkcs7SignatureGeneratorImplementsIDisposable PublicSharedReadOnlyFactoriesAsNewPkcs11InteropFactories() Private_pkcs11LibraryAsIPkcs11LibraryPrivate_slotAsISlotPrivate_sessionAsISessionPrivate_privateKeyHandleAsIObjectHandlePrivate_ckaLabelAsStringPrivate_ckaIdAsByte()Private_certificateAsX509Certificate2Private_hashAlgorithmAsOIDPrivate_hashDigestAsIDigest ''' <summary>''' Initializes a new instance of the Pkcs11SignatureGenerator class.''' </summary>PublicSubNew(ByVal libraryPath AsString, ByVal tokenSerial AsString, ByVal tokenLabel AsString, ByVal pin AsByte(), ByVal ckaLabel AsString, ByVal ckaId AsByte(), ByVal hashAlgorihtm AsOID)Init(libraryPath, tokenSerial, tokenLabel, pin, ckaLabel, ckaId, hashAlgorihtm)EndSub ProtectedOverridesSubFinalize()Dispose(False)MyBase.Finalize()EndSub ''' <summary>''' Releases resources used by this object.''' </summary>PublicSubDispose() ImplementsIDisposable.DisposeDispose(True)GC.SuppressFinalize(Me)EndSub ProtectedSubDispose(ByVal disposing AsBoolean)If disposing ThenIf_certificateIsNotNothingThen_certificate.Dispose()_certificate = NothingEndIfIf_sessionIsNotNothingThen_session.Dispose()_session = NothingEndIfIf_pkcs11LibraryIsNotNothingThen_pkcs11Library.Dispose()_pkcs11Library = NothingEndIfEndIfEndSub PrivateFunctionFindSlot(ByVal tokenSerial AsString, ByVal tokenLabel AsString) AsISlotIfString.IsNullOrEmpty(tokenSerial) AndAlsoString.IsNullOrEmpty(tokenLabel) ThenThrowNewArgumentException("Token serial and/or label has to be specified")EndIf Dim slots AsList(OfISlot) = _pkcs11Library.GetSlotList(SlotsType.WithTokenPresent)ForEach slot AsISlotIn slotsDim tokenInfo AsITokenInfo = Nothing Try tokenInfo = slot.GetTokenInfo()Catch ex AsPkcs11ExceptionIf ex.RV <> CKR.CKR_TOKEN_NOT_RECOGNIZEDAndAlso ex.RV <> CKR.CKR_TOKEN_NOT_PRESENTThenThrowEndIfEndTry If tokenInfo IsNothingThenContinueForEndIf IfNotString.IsNullOrEmpty(tokenSerial) ThenIfString.Compare(tokenSerial, tokenInfo.SerialNumber, StringComparison.InvariantCultureIgnoreCase) <> 0ThenContinueForEndIfEndIf IfNotString.IsNullOrEmpty(tokenLabel) ThenIfString.Compare(tokenLabel, tokenInfo.Label, StringComparison.InvariantCultureIgnoreCase) <> 0ThenContinueForEndIfEndIf Return slotNextReturnNothingEndFunction ProtectedSubInit(ByVal libraryPath AsString, ByVal tokenSerial AsString, ByVal tokenLabel AsString, ByVal pin AsByte(), ByVal ckaLabel AsString, ByVal ckaId AsByte(), ByVal hashAlgorihtm AsOID)IfString.IsNullOrEmpty(libraryPath) ThenThrowNewArgumentNullException($"Invalid library path ""{libraryPath}"".")EndIf Try_pkcs11Library = Factories.Pkcs11LibraryFactory.LoadPkcs11Library(Factories, libraryPath, AppType.SingleThreaded) _slot = FindSlot(tokenSerial, tokenLabel)If_slotIsNothingThenThrowNewException(String.Format("Token with serial ""{0}"" and label ""{1}"" was not found", tokenSerial, tokenLabel))EndIf _session = _slot.OpenSession(SessionType.ReadOnly)_session.Login(CKU.CKU_USER, pin) '' initialize _privateKeyHandle and _certificateUsing session AsISession = _slot.OpenSession(SessionType.ReadOnly)'' private keyDim searchTemplate AsNewList(OfIObjectAttribute)() searchTemplate.Add(Factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY)) searchTemplate.Add(Factories.ObjectAttributeFactory.Create(CKA.CKA_KEY_TYPE, CKK.CKK_RSA))IfNotString.IsNullOrEmpty(ckaLabel) Then searchTemplate.Add(Factories.ObjectAttributeFactory.Create(CKA.CKA_LABEL, ckaLabel))EndIfIf ckaId IsNotNothingThen searchTemplate.Add(Factories.ObjectAttributeFactory.Create(CKA.CKA_ID, ckaId))EndIf Dim foundObjects AsList(OfIObjectHandle) = session.FindAllObjects(searchTemplate)If foundObjects.Count < 1ThenThrowNewException(String.Format("Private key with label ""{0}"" and id ""{1}"" was not found.", ckaLabel, If(ckaId IsNothing, Nothing, ConvertUtils.BytesToHexString(ckaId))))ElseIf foundObjects.Count > 1ThenThrowNewException(String.Format("More than one private key with label ""{0}"" and id ""{1}"" was found.", ckaLabel, If(ckaId IsNothing, Nothing, ConvertUtils.BytesToHexString(ckaId))))EndIf_privateKeyHandle = foundObjects(0) '' certificate searchTemplate.Clear() searchTemplate.Add(Factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_CERTIFICATE))IfNotString.IsNullOrEmpty(ckaLabel) Then searchTemplate.Add(Factories.ObjectAttributeFactory.Create(CKA.CKA_LABEL, ckaLabel))EndIfIf ckaId IsNotNothingThen searchTemplate.Add(Factories.ObjectAttributeFactory.Create(CKA.CKA_ID, ckaId))EndIf foundObjects = session.FindAllObjects(searchTemplate)If foundObjects.Count = 1ThenDim attributes AsNewList(OfCKA)() attributes.Add(CKA.CKA_VALUE) Dim certificateAttributes AsList(OfIObjectAttribute) = session.GetAttributeValue(foundObjects(0), attributes)Dim certificateData AsByte() = certificateAttributes(0).GetValueAsByteArray()_certificate = NewX509Certificate2(certificateData)EndIfEndUsing _ckaLabel = ckaLabel_ckaId = ckaIdIf hashAlgorihtm = OID.HashAlgorithms.SHA1Then_hashDigest = NewSha1Digest()ElseIf hashAlgorihtm = OID.HashAlgorithms.SHA256Then_hashDigest = NewSha256Digest()ElseIf hashAlgorihtm = OID.HashAlgorithms.SHA384Then_hashDigest = NewSha384Digest()ElseIf hashAlgorihtm = OID.HashAlgorithms.SHA512Then_hashDigest = NewSha512Digest()ElseThrowNewException($"Unsupported HASH algorithm {hashAlgorihtm}.")EndIf_hashAlgorithm = hashAlgorihtmCatchIf_sessionIsNotNothingThen_session.Dispose()_session = NothingEndIfIf_pkcs11LibraryIsNotNothingThen_pkcs11Library.Dispose()_pkcs11Library = NothingEndIf ThrowEndTryEndSub ''' <summary>''' Gets the X509Certificate2 object found on the token''' with same ckaLabel and ckaId as a private key.''' </summary>PublicReadOnlyPropertyCertificateAsX509Certificate2GetReturn_certificateEndGetEndProperty ''' <summary>''' Gets the ID of the hash algorithm.''' </summary>PublicReadOnlyPropertyHashAlgorithmAsOIDImplementsIPkcs7SignatureGenerator.HashAlgorithmGetReturn_hashAlgorithmEndGetEndProperty ''' <summary>''' Gets the ID of the encryption algorithm.''' </summary>PublicReadOnlyPropertyDigestEncryptionAlgorithmAsOIDImplementsIPkcs7SignatureGenerator.DigestEncryptionAlgorithmGetReturnOID.EncryptionAlgorithms.RSAEndGetEndProperty ''' <summary>''' Signs data.''' </summary>PublicFunctionSignData(ByVal input AsByte()) AsByte() ImplementsIPkcs7SignatureGenerator.SignDataUsing session AsISession = _slot.OpenSession(SessionType.ReadOnly)Using mechanism AsIMechanism = Factories.MechanismFactory.Create(CKM.CKM_RSA_PKCS)Dim hash = NewByte(_hashDigest.GetDigestSize() - 1) {}_hashDigest.Reset()_hashDigest.BlockUpdate(input, 0, input.Length)_hashDigest.DoFinal(hash, 0) Dim derObjectIdentifier = NewDerObjectIdentifier(_hashAlgorithm.ID)Dim algorithmIdentifier = NewAlgorithmIdentifier(derObjectIdentifier, DerNull.Instance)Dim digestInfo = NewDigestInfo(algorithmIdentifier, hash)Dim digestInfoBytes AsByte() = digestInfo.GetDerEncoded() Return session.Sign(mechanism, _privateKeyHandle, digestInfoBytes)EndUsingEndUsingEndFunctionEndClass