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:

  1. 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.
  2. 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:
  • psesBasic: A 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’ time stamp request before starting the signing process to find out the exact size of the time stamp. Can be useful if the time stamp server you are using returns very large time stamps.
  • psesPredefinedSize: A window of PredefinedSignatureSize bytes is allocated. No built-in estimation is performed.

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.