EldoS | Feel safer!

Software components for data protection, secure storage and transfer

Implementing PAdES signing of PDF documents using SecureBlackbox

PAdES (PDF Advanced Electronic Signatures) standard extends the generic PDF signatures mechanism to address 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

PAdES-BES and PAdES-EPES signature types (ETSI TS 102-778-3), besides confirming the signer’s identity, provide additional cover for non-repudiation and guaranteed signing time. EPES subtype also allows 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

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 be ceased. CAs might close or restructure due to market disturbances. Moreover, cryptographic algorithms expire as well, becoming weaker and less resistant to attacks.

Signatures that are supposed to remain valid and validateable 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:

  1. when producing a signature, the signer embeds to 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.
  2. the document is updated regularly to respond to changes in cryptographic environment. During such updates an external timestamping 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 timestamp (ETSI TS 102-778-4), to the end of the archived document. An archived document thus may contain a collection of document timestamp signatures, each of which certifies the document and all the preceding signatures with more robust algorithm and key.

Creating a long-term signature

As one can conclude from the above, 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 timestamp 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 timestamp

To maintain a long-term signed document ‘in good shape’ you will occasionally need to add document timestamps to it to extend the validity period of the signature. Document timestamps are normally added just before the certificate that was used to create the preceding signature expires (including the preceding document timestamp), 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 timestamp 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’s not. This way, it always converts a non-long-term signature to a long-term one before adding a document timestamp to it.

Preparing and Tuning the TElPDFAdvancedPublicKeySecurityHandler object

Each PKI environment is different, so you will probably need to tune up the TElPDFAdvancedPublicKeySecurityHandler object to match your particular one. 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 can’t 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, Document Timestamp).
AutoCollectRevocationInfo Tells the component to attempt to automatically collect revocation information from online sources.
IgnoreChainValidationErrors Prevents the handler from throwing exceptions in 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 to tune-up various minor aspects of PAdES and inner CMS generation.
SignatureSizeEstimationStrategy Tells the component how exactly the size of signature window should be estimated:
  • psesBasic: very fast and rough estimation based on the component’s knowledge of typical validation element sizes. Suitable for most environments, but may expose problems in environments with extraordinal element sizes (e.g. CRLs of 200KB);
  • psesSmart: a smarter yet slower estimation strategy. Works well with both typical- and peculiar-sized validation elements.
  • psesSmartAndTrialTimestamp: same as psesSmart, but performs a ‘trial’ timestamp request before starting the signing process to find out the exact size of the timestamp. Can be useful if the timestamp server you are using returns very large timestamps.
  • psesPredefinedSize: a window of PredefinedSignatureSize bytes is allocated, no built-in estimation is performed.

Registering PAdES handlers

In 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 automatic creation and attachment of generic PDF handlers to basic signature objects when the document is opened (PAdES-BES and document timestamp signatures will be assigned with proper PAdES handlers). As a result, you will be unable to convert basic signatures to long-term ones with the Update() operation or use 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

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 build against .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’ve 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.

Return to the list

|

Back to top

As of July 15, 2016 EldoS Corporation will operate as a division of /n software inc. For more information, please read the announcement.

Got it!