SecureBlackbox 16: Implementing the PAdES Signing of PDF Documents Using SecureBlackbox
The PAdES (PDF Advanced Electronic Signatures) standard extends the generic PDF signature mechanism to address the increasing demand for native long-term digital signature capabilities. The standard in particular introduces provisions for signature archival and update operations. Generally speaking, PAdES signatures are not compatible with standard PDF signatures and in SecureBlackbox are handled by a separate TElPDFAdvancedPublicKeySecurityHandler component. The component is available for users holding both PDFBlackbox and PKIBlackbox licenses.
Creating Basic (PAdES-Basic) Signatures
A PAdES-Basic signature (ETSI TS 102-778-2) is de facto a "classic" generic PDF signature. It was included to the PAdES standard for backward-compatibility purposes. This type of signature does not provide long-term validity capabilities. To create a basic signature, set the PAdESSignatureType property of the PAdES handler to pastBasic.
[Delphi]
Doc := TElPDFDocument.Create(nil); try // Opening the document. Doc.Open(docStream); // Adding new signature. Index := Doc.AddSignature(); Sig := Doc.Signatures[Index]; Sig.SigningTime := UTCNow; // Creating a handler and assigning it to the new signature object. Handler := TElPDFAdvancedPublicKeySecurityHandler.Create(nil); Try Sig.Handler := Handler; // CertStorage should contain at least one certificate // with an associated private key. Handler.CertStorage := CertStorage; // Basic signature type. Handler.PAdESSignatureType := pastBasic; // Adobe PDF security filter name. Handler.CustomName := ‘Adobe.PPKLite’; // Saving and closing the document. Doc.Close(true); finally FreeAndNil(Handler); end; finally FreeAndNil(Doc); end;
[C#]
TElPDFDocument doc = new TElPDFDocument(); try { // Opening the document. doc.Open(docStream); // Adding new signature. int index = doc.AddSignature(); TElPDFSignature sig = doc.get_Signatures(index); sig.SigningTime = DateTime.UtcNow; // Creating a handler and assigning it to the new signature object. TElPDFAdvancedPublicKeySecurityHandler handler = new TElPDFAdvancedPublicKeySecurityHandler(); try { sig.Handler = handler; // certStorage should contain at least one certificate // with an associated private key. handler.CertStorage = certStorage; // Basic signature type. handler.PAdESSignatureType = TSBPAdESSignatureType.pastBasic; // Adobe PDF security filter name. handler.CustomName = “Adobe.PPKLite”; // Saving and closing the document. doc.Close(true); } finally { handler.Dispose(); } } finally { doc.Dispose(); }
Creating Enhanced (PAdES-BES and PAdES-EPES) Signatures
The PAdES-BES and PAdES-EPES signature types (ETSI TS 102-778-3), besides confirming the signer's identity, provide additional cover for nonrepudiation and a guaranteed time of signing. The EPES subtype also allows you to reference a signature policy in accordance with which the signature should be validated. To create a BES or EPES signature, set the PAdESSignatureType property of the handler to pastEnhanced.
[Delphi]
Doc := TElPDFDocument.Create(nil); try // Opening the document. Doc.Open(docStream); // Adding new signature. Index := Doc.AddSignature(); Sig := Doc.Signatures[Index]; Sig.SigningTime := UTCNow; // Creating a handler and assigning it to the new signature object. Handler := TElPDFAdvancedPublicKeySecurityHandler.Create(nil); Try Sig.Handler := Handler; // CertStorage should contain at least one certificate // with an associated private key. Handler.CertStorage := CertStorage; // Enhanced signature type. Handler.PAdESSignatureType := pastEnhanced; // Adobe PDF security filter name. Handler.CustomName := ‘Adobe.PPKLite’; // Saving and closing the document. Doc.Close(true); finally FreeAndNil(Handler); end; finally FreeAndNil(Doc); end;
[C#]
TElPDFDocument doc = new TElPDFDocument(); try { // Opening the document. doc.Open(docStream); // Adding new signature. int index = doc.AddSignature(); TElPDFSignature sig = doc.get_Signatures(index); sig.SigningTime = DateTime.UtcNow; // Creating a handler and assigning it to the new signature object. TElPDFAdvancedPublicKeySecurityHandler handler = new TElPDFAdvancedPublicKeySecurityHandler(); try { sig.Handler = handler; // certStorage should contain at least one certificate // with an associated private key. handler.CertStorage = certStorage; // Basic signature type. handler.PAdESSignatureType = TSBPAdESSignatureType.pastEnhanced; // Adobe PDF security filter name. handler.CustomName = “Adobe.PPKLite”; // Saving and closing the document. doc.Close(true); } finally { handler.Dispose(); } } finally { doc.Dispose(); }
Long-Term PDF Signatures
The PKI environment changes. Validation elements, such as certificates, CRLs and OCSP responses, expire with time. Online validation services that existed at the moment of signing might eventually cease. CAs might close or restructure due to market disturbances. Cryptographic algorithms expire as well, becoming weaker and less resistant to attacks.
Signatures that are supposed to remain valid and verifiable for extremely long periods of time have to minimize their dependency on the state of the PKI environment at the moment of signing. This is generally achieved by the two methods that follow:
- When producing a signature, the signer embeds into it all the validation elements that might help the verifier to establish the validity of the signature without contacting online validation sources. Those elements include the entire certificate chain (up to the root certificate), and all the CRLs and OCSP responses that confirm the validity of all the certificates forming the chain at the moment of signing.
- The document is updated regularly to respond to changes in the cryptographic environment. During such updates an external time-stamping service is used for certifying the contents of the document and the time of update with an effective certificate and robust (at that moment of time) cryptographic algorithm. The certification is made by appending an additional signature, called a document time stamp (ETSI TS 102-778-4), to the end of the archived document. An archived document thus may contain a collection of document time stamp signatures, each of which certifies the document and all the preceding signatures with a more robust algorithm and key.
Creating a long-term signature
As one can conclude from the above, the creation of a long-term signature starts with creation of a basic or BES/EPES signature that has all the validation information included. The signature must contain a time stamp from a trusted TSA that confirms the time of signing:
[Delphi]
Doc := TElPDFDocument.Create(nil); try // Opening the document. Doc.Open(docStream); // Adding new signature. Index := Doc.AddSignature(); Sig := Doc.Signatures[Index]; Sig.SigningTime := UTCNow; // Creating a handler and assigning it to the new signature object. Handler := TElPDFAdvancedPublicKeySecurityHandler.Create(nil); try Sig.Handler := Handler; // CertStorage should contain at least one certificate // with an associated private key. Handler.CertStorage := CertStorage; // Enhanced signature type. Handler.PAdESSignatureType := pastEnhanced; // Configuring the handler to make it perform deep chain validation // and collect all available revocation information from // online sources. Handler.AutoCollectRevocationInfo := true; Handler.ForceCompleteChainValidation := true; Handler.IncludeRevocationInfoToAdbeAttribute := true; // Use the CustomRevocationInfo property to provide the handler // with revocation information not available online: Handler.CustomRevocationInfo.Certificates.Add(IntmCACert, false); Handler.CustomRevocationInfo.Certificates.Add(RootCACert, false); Idx := Handler.CustomRevocationInfo.AddCRL(); Handler.CustomRevocationInfo.CRLs[Idx].Assign(RootCACrl); // Adobe PDF security filter name. Handler.CustomName := ‘Adobe.PPKLite’; // Creating and configuring TSP components. HttpClient := TElHTTPSClient.Create(nil); try TspClient := TElHTTPTSPClient.Create(nil); try TspClient.HTTPClient := HttpClient; TspClient.URL := ‘http://tsa.myserver.com’; Handler.TSPClient := TspClient; // Saving and closing the document. Doc.Close(true); finally FreeAndNil(TspClient); end; finally FreeAndNil(HttpClient); end; finally FreeAndNil(Handler); end; finally FreeAndNil(Doc); end;
[C#]
TElPDFDocument doc = new TElPDFDocument(); try { // Opening the document. doc.Open(docStream); // Adding new signature. int index = doc.AddSignature(); TElPDFSignature sig = doc.get_Signatures(index); sig.SigningTime = DateTime.UtcNow; // Creating a handler and assigning it to the new signature object. TElPDFAdvancedPublicKeySecurityHandler handler = new TElPDFAdvancedPublicKeySecurityHandler(); try { sig.Handler = handler; // certStorage should contain at least one certificate // with an associated private key. handler.CertStorage = certStorage; // Enhanced signature type. handler.PAdESSignatureType = TSBPAdESSignatureType.pastEnhanced; // Configuring the handler to make it perform deep chain validation // and collect all available revocation information from // online sources. handler.AutoCollectRevocationInfo = true; handler.ForceCompleteChainValidation = true; handler.IncludeRevocationInfoToAdbeAttribute = true; // Use the CustomRevocationInfo property to provide the handler // with revocation information not available online: handler.CustomRevocationInfo.Certificates.Add(intmCACert, false); handler.CustomRevocationInfo.Certificates.Add(rootCACert, false); index = handler.CustomRevocationInfo.AddCRL(); handler.CustomRevocationInfo.get_CRLs(index).Assign(rootCACrl); // Adobe PDF security filter name. handler.CustomName = “Adobe.PPKLite”; // Creating and configuring TSP components. TElHTTPSClient httpClient = new TElHTTPSClient(); try { TElHTTPTSPClient tspClient = new TElHTTPTSPClient(); try { tspClient.HTTPClient = httpClient; tspClient.URL = “http://tsa.myserver.com”; handler.TSPClient = tspClient; // Saving and closing the document. doc.Close(true); } finally { tspClient.Dispose(); } } finally { httpClient.Dispose(); } } finally { handler.Dispose(); } } finally { doc.Dispose(); }
The document signed in the above way can be validated without contacting online validation sources, as the verifier can extract all the needed validation pieces from the signature.
Archiving PDF Documents with a Document Time Stamp
To maintain a long-term signed document, you will occasionally need to add document time stamps to it to extend the validity period of the signature. Document time stamps are normally added just before the certificate that was used to create the preceding signature expires (including the preceding document time stamp), or just before the cryptographic algorithm used to create the preceding signature is declared deprecated (note that it can be a signature policy that deprecates an algorithm, not only governments or cryptoanalysts). A document time stamp can be added in the following way:
[Delphi]
Doc := TElPDFDocument.Create(nil); try // Opening the document. Doc.Open(docStream); // Obtaining the last signature object. Index := Doc.SignatureCount - 1; Sig := Doc.Signatures[Index]; // Checking if the signature references the correct handler. if not (Sig.Handler is TElPDFAdvancedPublicKeySecurityHandler) then raise Exception.Create(‘Wrong security handler, PAdES is likely not to be initialized’); // Ensuring that the existing signature contains the complete set // of revocation elements. Handler := TElPDFAdvancedPublicKeySecurityHandler(Sig.Handler); // Configuring the handler to make it perform deep chain validation // and collect all available revocation information from // online sources. Handler.AutoCollectRevocationInfo := true; Handler.ForceCompleteChainValidation := true; // Use the CustomRevocationInfo property to provide the handler // with revocation information not available online: Handler.CustomRevocationInfo.Certificates.Add(IntmCACert, false); Handler.CustomRevocationInfo.Certificates.Add(RootCACert, false); Index := Handler.CustomRevocationInfo.AddCRL(); Handler.CustomRevocationInfo.CRLs[Index].Assign(RootCACrl); // Updating the signature (collecting missing revocation elements // and adding them to the document). Sig.Update(); // Adding document timestamp. Index := Doc.AddSignature(); Sig := Doc.Signatures[Index]; Sig.SigningTime := UTCNow; // Creating a handler and assigning it to the new signature object. Handler := TElPDFAdvancedPublicKeySecurityHandler.Create(nil); try Sig.Handler := Handler; Handler.PAdESSignatureType := pastDocumentTimestamp; // Creating and configuring TSP components. HttpClient := TElHTTPSClient.Create(nil); try TspClient := TElHTTPTSPClient.Create(nil); try TspClient.HTTPClient := HttpClient; TspClient.URL := ‘http://tsa.myserver.com’; Handler.TSPClient := TspClient; // Saving and closing the document. Doc.Close(true); finally FreeAndNil(TspClient); end; finally FreeAndNil(HttpClient); end; finally FreeAndNil(Handler); end; finally FreeAndNil(Doc); end;
[C#]
TElPDFDocument doc = new TElPDFDocument(); try { // Opening the document. doc.Open(docStream); // Obtaining the last signature object. int index = doc.SignatureCount - 1; TElPDFSignature sig = doc.get_Signatures(Idx); // Checking if the signature references the correct handler. if (!(sig.Handler is TElPDFAdvancedPublicKeySecurityHandler)) { throw new Exception(“Wrong security handler, PAdES is likely not to be initialized”); } // Ensuring that the existing signature contains the complete set // of revocation elements. TElPDFAdvancedPublicKeySecurityHandler handler = (TElPDFAdvancedPublicKeySecurityHandler)(sig.Handler); // Configuring the handler to make it perform deep chain validation // and collect all available revocation information from // online sources. handler.AutoCollectRevocationInfo = true; handler.ForceCompleteChainValidation = true; // Use the CustomRevocationInfo property to provide the handler // with revocation information not available online: handler.CustomRevocationInfo.Certificates.Add(intmCACert, false); handler.CustomRevocationInfo.Certificates.Add(rootCACert, false); index = Handler.CustomRevocationInfo.AddCRL(); handler.CustomRevocationInfo.get_CRLs(index).Assign(rootCACrl); // Updating the signature (collecting missing revocation elements // and adding them to the document). sig.Update(); // Adding document timestamp. index = doc.AddSignature(); sig = doc.get_Signatures(index); sig.SigningTime = DateTime.UtcNow; // Creating a handler and assigning it to the new signature object. handler = new TElPDFAdvancedPublicKeySecurityHandler(); try { sig.Handler = handler; handler.PAdESSignatureType = TSBPAdESSignatureType.pastDocumentTimestamp; // Creating and configuring TSP components. httpClient = new TElHTTPSClient(); try { tspClient = new TElHTTPTSPClient(); try { tspClient.HTTPClient = httpClient; tspClient.URL = “http://tsa.myserver.com”; handler.TSPClient = tspClient; // Saving and closing the document. doc.Close(true); } finally { tspClient.Dispose(); } } finally { httpClient.Dispose(); } } finally { handler.Dispose(); } } finally { doc.Dispose(); }
The first part of the above code ensures that all the revocation information is available for the existing signature and collects it if it is not. This way, it always converts a non-long-term signature to a long-term one before adding a document time stamp to it.
Preparing and Tuning the TElPDFAdvancedPublicKeySecurityHandler Object
Each PKI environment is different, so you will probably need to fine-tune the TElPDFAdvancedPublicKeySecurityHandler object to match your particular environment. The table below gives a breakdown of the most important properties of the handler object.
CustomRevocationInfo | Use this property to pass your own revocation information (certificates, CRLs, and OCSP responses) to the component if it cannot be obtained automatically from online sources. |
CMS | Provides access to the underlying TElSignedCMSMessage object and can be used to fine-tune inner CMS properties. |
PAdESSignatureType | Specifies the subtype of PAdES signature to create (PAdES-basic, PAdES-BES/EPES, or Document Timestamp). |
AutoCollectRevocationInfo | Tells the component to attempt to automatically collect revocation information from online sources. |
IgnoreChainValidationErrors | Prevents the handler from throwing exceptions in the case of chain validation errors. Use this property with care in real-world environments, as ignoring certain validation problems may pose a security risk. |
ForceCompleteChainValidation | Controls whether the component should validate the whole certificate chain up to the trusted certificate. Can be switched off for debug or testing purposes to suppress validation exceptions. |
IncludeRevocationInfoToAdbeAttribute | Specifies if revocation elements should be included to the document as a legacy adbeRevocationInfoArchival attribute, in addition to the methods offered by PAdES (mainly for compatibility purposes). |
PAdESOptions | Allows you to fine-tune various minor aspects of PAdES and inner CMS generation. |
SignatureSizeEstimationStrategy |
Tells the component how exactly the size of a signature window should be estimated:
|
Registering PAdES Handlers
In the default component configuration, the priority of PAdES security handlers when processing the documents is lower than that of generic PDF handlers. While not affecting document signing operations, this leads to the automatic creation and attachment of generic PDF handlers to basic signature objects when the document is opened (PAdES-BES and document time stamp signatures will be assigned with the proper PAdES handlers). As a result, you will be unable to convert basic signatures to long-term ones with the Update operation or use the chain validation capabilities of PAdES handlers to validate basic signatures. To force the components to always create PAdES handlers for all supported types of signatures, including basic ones, PAdES handlers should be moved up in the registered handlers list. This can be done with the following code:
UnregisterSecurityHandler(TElPDFPublicKeySecurityHandler); UnregisterSecurityHandler(TElPDFAdvancedPublicKeySecurityHandler); RegisterSecurityHandler(TElPDFAdvancedPublicKeySecurityHandler); RegisterSecurityHandler(TElPDFPublicKeySecurityHandler);
Registering Revocation Retriever Factories
The TElPDFAdvancedPublicKeySecurityHandler and TElX509CertificateValidator classes use revocation information retrievers to obtain various pieces of validation information during the deep validation process. The retrievers are created by four object factories, responsible for different kinds of online services. In most public PKI environments, it is enough to register the HTTP OCSP and HTTP CRL factories, while private and corporate environments might require LDAP connectivity as well. In projects built against the .NET, Java, and C++ product editions, the factories should be populated manually with the following code:
// Registering HTTP OCSP client SBHTTPOCSPClient.Unit.RegisterHTTPOCSPClientFactory(); // Registering HTTP CRL retriever SBHTTPCRL.Unit.RegisterHTTPCRLRetrieverFactory(); // Registering LDAP CRL retriever SBLDAPCRL.Unit.RegisterLDAPCRLRetrieverFactory(); // Registering LDAP certificate retriever SBLDAPCertRetriever.Unit.RegisterLDAPCertificateRetrieverFactory();
In Delphi environments, the retrievers are registered automatically, provided that you have referenced the corresponding units in your project’s or form’s uses clause:
uses SBHTTPOCSPClient, SBHTTPCRL, SBLDAPCRL, SBLDAPCertRetriever;
References
[PADES]: Electronic Signatures and Infrastructures (ESI); PDF Advanced Electronic Signature Profiles; Parts 1-5, ETSI TS 102 778-1, -2, -3, -4, -5.
We appreciate your feedback. If you have any questions, comments, or suggestions about this article please contact our support team at support@nsoftware.com.