SecureBlackbox 16: Implementing XAdES Signing of Data Using SecureBlackbox

Note: This article applies only to SecureBlackbox Legacy. For future development please consider using the latest version.

Working with XAdES

XAdES (XML advanced electronic signatures) are built upon XML-DSig (XML digital signatures) by adding additional information qualifying the signature and the signed data. This qualifying information is arranged in so-called "qualifying properties," which are added to the signature as a data object. Qualifying properties consist of signed and unsigned properties.

SecureBlackbox has the following components to manage XML-DSig and XAdES signatures: The TElXMLSigner and TElXMLVerifier components are capable of creating and verifying XML-DSig signatures. The TElXAdESSigner and TElXAdESVerifier components are capable of creating, extending, and verifying XAdES info. They are used together with the TElXMLSigner or TElXMLVerifier components.

The XAdES components support all XAdES versions: 1.1.1, 1.2.2, 1.3.2, and 1.4.1 (aka 1.4.2) and all XAdES forms: XAdES, XAdES-BES , -EPES, -T, -C, -X, -X-L, and -A. They can also extend the XAdES signature of a "lower" form (e.g., XAdES-EPES) to an "upper" form (e.g., XAdES-A).

Below you will find the code snippets that illustrate how to create and extend general XAdES signatures and a collection of code snippets that illustrate creating/extending each XAdES form.


Content

Creating XAdES Signatures

This sample shows the creation of an enveloped XML-DSig signature with XAdES info, though the same components can be used to sign any type of data. The XML-DSig signature may be detached from or attached to signed data, may be enveloping (when it contains the signed data within itself), or enveloped (when it comprises a part of the document containing the signed data).

[Delphi]

// Creating an instance of the XML-DSig signer.
Signer := TElXMLSigner.Create(nil);
// Creating an instance of the XAdES signer.
XAdESSigner := TElXAdESSigner.Create(nil);
// Setup the XAdES processor
Signer.XAdESProcessor := XAdESSigner;
try
  // Adding references. For example, add a reference for a document element.
  Ref := TElXMLReference.Create();
  Ref.TransformChain.Add(TElXMLEnvelopedSignatureTransform.Create);
  Ref.URI := '';
  Ref.URINode := XMLDocument.DocumentElement;
  Signer.References.Add(Ref);

  // Setup Signer options.
  // For example, using the default ones: enveloped signature, RSA-SHA1 signature method, etc.

  // Setup signer key data
  Signer.KeyData := X509Data;

  // calculate the digest value for references
  Signer.UpdateReferencesDigest();

  // Filling XAdES info
  // Setting the XAdES version
  XAdESSigner.XAdESVersion := XAdES_v1_4_1;

  // Place code to setup Signed properties and Timestamp client
  // [XAdES PLACE #1]

  // Generating the XAdES structure. Specify the desired XAdES form as a parameter
  XAdESSigner.Generate(XAdES_BES);

  // Generating the signature structure
  Signer.GenerateSignature();

  // Signing and saving the signature into the document element
  Signer.SaveEnveloped(XMLDocument.DocumentElement);

  // Place code to extend the XAdES form immediately after signing.
  // Used, for example, if you want to specify your own revocation info, not the autocollected one.
  // [XAdES PLACE #2]

finally
  FreeAndNil(Signer);
  FreeAndNil(XAdESSigner);
end;

[C#]

  // Creating an instance of the XML-DSig signer.
  TElXMLSigner Signer = new TElXMLSigner();
  // Creating an instance of the XAdES signer.
  TElXAdESSigner XAdESSigner = new TElXAdESSigner();
  // Setup the XAdES processor
  Signer.XAdESProcessor = XAdESSigner;
  try
  {
    // Adding references. For example, adding a reference for a document element.
    TElXMLReference Ref = new TElXMLReference();
    Ref.TransformChain.Add(new TElXMLEnvelopedSignatureTransform());
    Ref.URI = "";
    Ref.URINode = XMLDocument.DocumentElement;
    Signer.References.Add(Ref);

    // Setup Signer options.
    // For example, using default ones: enveloped signature, RSA-SHA1 signature method, etc.

    // Setup signer key data
    Signer.KeyData = X509Data;

    // Calculate the digest value for references
    Signer.UpdateReferencesDigest();

    // Filling XAdES info
    // Setting the XAdES version
    XAdESSigner.XAdESVersion = SBXMLAdES.Unit.XAdES_v1_4_1;

    // Place code to setup Signed properties and Timestamp client
    // [XAdES PLACE #1]

    // Generating the XAdES structure. Specify the desired XAdES form as a parameter
    XAdESSigner.Generate(SBXMLAdES.Unit.XAdES_BES);

    // Generating the signature structure
    Signer.GenerateSignature();

    // Signing and saving the signature into the document element
    Signer.SaveEnveloped(XMLDocument.DocumentElement);

    // Place code to extend the XAdES form immediately after signing.
    // Used, for example, if you want to specify your own revocation info, not the autocollected one.
    // [XAdES PLACE #2]
  }
  finally
  {
    Signer.Dispose();
    XAdESSigner.Dispose();
  }

Extending XAdES Signatures

[Delphi]

   // Creating an instance of the XML-DSig verifier.
   Verifier := TElXMLVerifier.Create(nil);
   // Creating an instance of the XAdES verifier.
   XAdESVerifier := TElXAdESVerifier.Create(nil);
   // Setup the XAdES processor
   Verifier.XAdESProcessor := XAdESVerifier;
   try
     // Load a signature from a specific element
     Verifier.Load(SignatureElement);

     // Validate a signature using the Verifier.ValidateSignature method
     // Validate references using the Verifier.ValidateReferences or ValidateReference methods

     // Check if a signature has XAdES info
     if XAdESVerifier.IsEnabled then
     begin
       // Validate a signer certificate and time stamps using the XAdESVerifier.Validate method

       // Place code to extend the XAdES form. For example, add a new archive time stamp.
       // [XAdES PLACE #3]
     end;
   finally
     FreeAndNil(Verifier);
     FreeAndNil(XAdESVerifier);
   end;
 

[C#]

  // Creating an instance of the XML-DSig verifier.
  TElXMLVerifier Verifier = new TElXMLVerifier();
  // Creating an instance of the XAdES verifier.
  TElXAdESVerifier XAdESVerifier = new TElXAdESVerifier();
  // Setup the XAdES processor
  Verifier.XAdESProcessor = XAdESVerifier;
  try
  {
    //Load a signature from a specific element
    Verifier.Load(SignatureElement);

    // Validate a signature using the Verifier.ValidateSignature method
    // Validate references using the Verifier.ValidateReferences or ValidateReference methods

    // Check if a signature has XAdES info
    if (XAdESVerifier.IsEnabled)
    {
      // Validate a signer certificate and time stamps using the XAdESVerifier.Validate method

      // Place code to extend the XAdES form. For example, add a new archive time stamp.
      // [XAdES PLACE #3]
    }
  }
  finally
  {
    Verifier.Dispose();
    XAdESVerifier.Dispose();
  }

In the code above, the code for setting XAdES signed properties (the XAdES-BES and -EPES forms) can be placed only in [XAdES PLACE #1], that is, before signing. The code for setting XAdES unsigned properties like CounterSignature and time stamps can be placed in any [XAdES PLACE]. The code for setting XAdES unsigned properties like adding custom revocation information (if not, autocollection is used) can be placed only in [XAdES PLACE #2] and [XAdES PLACE #3].

The XMLDocument object should contain the XML document. The X509Data object should contain a valid signing certificate with a private key. The SignatureElement object should contain a signature element. These requirements apply to all sample code snippets below.


Creating Basic Electronic Signatures (XAdES-BES)

Provides basic authentication and integrity protection and satisfies the legal requirements for advanced electronic signatures as defined in Directive 1999/93/EC of the European Parliament and of the Council of 13 December 1999 on a Community framework for electronic signatures. The XAdES-BES form (version 1.2.2 or higher) is equivalent to the XAdES form in version 1.1.1.

[Delphi]

// setting up the production place
XAdESSigner.Included := [xipProductionPlace];
XAdESSigner.ProductionPlace.City := 'City';
XAdESSigner.ProductionPlace.StateOrProvince := 'State';
XAdESSigner.ProductionPlace.PostalCode := 'Postal code';
XAdESSigner.ProductionPlace.CountryName := 'Country';

// adding claimed roles as text
XAdESSigner.Included := XAdESSigner.Included + [xipSignerRole];
XAdESSigner.SignerRole.ClaimedRoles.AddText(XAdESSigner.XAdESVersion,
  XMLDocument, 'Claimed Roles text');

// adding the signing certificates
// For this we need to create a memory certificate storage
CertStorage := TElMemoryCertStorage.Create(nil);
// Then add a signing certificate into it
CertStorage.Add(SigningCertificate, false);
// Setting up signing certificate storage
XAdESSigner.SigningCertificates := CertStorage;

// Set the signing time
XAdESSigner.SigningTime := UtcNow;

// Generate the XAdES structure
XAdESSigner.Generate(XAdES_BES);

[C#]

// Setting up the production place
XAdESSigner.Included = SBXMLAdESIntf.Unit.xipProductionPlace;
XAdESSigner.ProductionPlace.City = "City";
XAdESSigner.ProductionPlace.StateOrProvince = "State";
XAdESSigner.ProductionPlace.PostalCode = "Postal code";
XAdESSigner.ProductionPlace.CountryName = "Country";

// Adding claimed roles as text
XAdESSigner.Included |= SBXMLAdESIntf.Unit.xipSignerRole;
XAdESSigner.SignerRole.ClaimedRoles.AddText(XAdESSigner.XAdESVersion,
  XMLDocument, "Claimed Roles text");

// Adding the signing certificates
// For this we need to create a memory certificate storage
CertStorage = new TElMemoryCertStorage.Create();
// Then add a signing certificate into it
CertStorage.Add(SigningCertificate, false);
// Setting up the signing certificate storage
XAdESSigner.SigningCertificates = CertStorage;

// Set the signing time
XAdESSigner.SigningTime = DateTime.UtcNow;

// Generate the XAdES structure
XAdESSigner.Generate(XAdES_BES);

Below are additional properties of the XAdESSigner component that can be used while creating the XAdES-BES form:

  • SigningCertificatesDigestMethod: Specify a signing certificate digest method (default SHA-1).
  • OwnSigningCertificates: Force a component to free SigningCertificates storage.

Adding a CounterSignature (XAdES-BES)

Countersignatures are signatures that are applied one after the other and are used where the order the signatures are applied in is important. In these situations the first signature signs the signed data object. Each additional signature may sign in turn the latest previously generated signature, or all the previously generated signatures and the signed document. In the XAdES standard, countersignatures are embedded within the signatures that they countersign.

[Delphi]

  // Creating an instance of the XML-DSig signer
  CSigner := TElXMLSigner.Create(nil);
  // Adding custom references...
  // CSigner.UpdateReferencesDigest();
  // Adding references pointing to elements inside the main signature
  // (adding a reference to the SignatureValue element)
  Ref := TElXMLReference.Create();
  Ref.RefType := xmlRefTypeCountersignedSignature;
  // Set a URI for the SignatureValue element
  Ref.URI := '#SignatureValue-0';
  CSigner.References.Add(Ref);

  // Setup signer key data
  CSigner.KeyData := X509KeyData;
  // Setup CSigner options.
  // For example, using default ones: RSA-SHA1 signature method, etc.

  // Add countersignature
  XAdESSigner.AddCounterSignature(CSigner);

  // Generate the signature structure (continue creating the XAdES signature sample)
  Signer.GenerateSignature();
  ...
  // Set an ID for the SignatureValue element
  Signer.Signature.SignatureValue.ID := 'SignatureValue-0';

[C#]

  // Creating an instance of the XML-DSig signer
  TElXMLSigner CSigner = new TElXMLSigner();
  // Adding custom references...
  // CSigner.UpdateReferencesDigest();
  // Adding references pointing to elements inside the main signature
  // (adding a reference to the SignatureValue element)
  TElXMLReference Ref = new TElXMLReference();
  Ref.RefType = SBXMLDefs.Unit.xmlRefTypeCountersignedSignature;
  // Set a URI for SignatureValue element
  Ref.URI = "#SignatureValue-0";
  CSigner.References.Add(Ref);

  // Setup signer key data
  CSigner.KeyData = X509KeyData;
  // Setup CSigner options. For example, using default ones:
  // RSA-SHA1 signature method and etc.

  // Add countersignature
  XAdESSigner.AddCounterSignature(CSigner);

  // Generate the signature structure (continue creating the XAdES signature sample)
  Signer.GenerateSignature();
  ...
  // Set an ID for the SignatureValue element
  Signer.Signature.SignatureValue.ID = "SignatureValue-0";

Creating Explicit Policy Electronic Signatures (XAdES-EPES)

A XAdES-EPES builds on the XML-DSig or XAdES-BES forms by incorporating the SignaturePolicyIdentifier element. A signature policy is useful to clarify the precise role and commitments that the signer intends to assume with respect to the signed data object and to avoid claims by the verifier that a different signature policy was implied by the signer.

The electronic signature can contain an explicit and unambiguous identifier of a signature policy together with a hash value of the signature policy:

[Delphi]

  // Setting the policy description
  XAdESSigner.PolicyId.SigPolicyId.Description := 'Description text';
  // Setting the policy identifier
  XAdESSigner.PolicyId.SigPolicyId.Identifier := 'http://...';
  XAdESSigner.PolicyId.SigPolicyId.IdentifierQualifier := xqtOIDAsURI;
  // Calculate the signature policy hash value using the SHA-1 digest method
  XAdESSigner.PolicyId.SigPolicyHash.DigestMethod := DigestMethodToURI(xdmSHA1);
  XAdESSigner.PolicyId.SigPolicyHash.DigestValue :=
      CalculateDigest(@Buf[0], Length(Buf), xdmSHA1);

  // Generate the XAdES structure
  XAdESSigner.Generate(XAdES_EPES);

[C#]

  // Setting the policy description
  XAdESSigner.PolicyId.SigPolicyId.Description = "Description text";
  // Setting the policy identifier
  XAdESSigner.PolicyId.SigPolicyId.Identifier = "http://...";
  XAdESSigner.PolicyId.SigPolicyId.IdentifierQualifier = SBXMLAdES.Unit.xqtOIDAsURI;
  // Calculate the signature policy hash value using the SHA1 digest method
  XAdESSigner.PolicyId.SigPolicyHash.DigestMethod =
    SBXMLSec.Unit.DigestMethodToURI(SBXMLSec.Unit.xdmSHA1);
  XAdESSigner.PolicyId.SigPolicyHash.DigestValue =
    SBXMLSec.Unit.CalculateDigest(buf, SBXMLSec.Unit.xdmSHA1);

  // Generate the XAdES structure
  XAdESSigner.Generate(SBXMLAdES.Unit.XAdES_EPES);

In the preceding code example, the buf object is a byte array that contains policy data/the document (for example, downloaded from the specified URI).

Alternatively, the electronic signature can avoid the inclusion of the aforementioned identifier and hash value. In this case you need just to call the XAdESSigner.Generate method with the XAdES_EPES parameter.


Creating Electronic Signatures with Time (XAdES-T)

The XAdES-T form extends the XAdES-BES or XAdES-EPES forms with an external time stamp that certifies the time of signing. To be able to create XAdES-T signatures you must have access to an online TSA service that is capable of producing RFC3161-compliant time stamps.

[Delphi]

  // Creating the time-stamping components.
  HttpClient := TElHTTPSClient.Create(nil);
  // (!) Remember to handle the HttpClient's OnCertificateValidate
  // event if the TSA server is accessible via the HTTPS protocol.

  TspClient := TElHTTPTSPClient.Create(nil);
  try
    TspClient.HTTPClient := HttpClient;
    TspClient.URL := 'http://tsa.myserver.com';
    HttpClient.SocketTimeout := 20000; // 20 seconds

    // Adding a signature time stamp
    k := XAdESSigner/XAdESVerifier.AddSignatureTimestamp(TspClient);
    if k <> 0 then
      raise Exception.CreateFmt('Failed to time-stamp: %d', [k]);

  finally
    // Free components
    FreeAndNil(TspClient);
    FreeAndNil(HttpClient);
  end;

[C#]

  // Creating time-stamping components.
  httpClient = new TElHTTPSClient();
  // (!) Remember to handle the httpClient's OnCertificateValidate
  // event if the TSA server is accessible via the HTTPS protocol.

  tspClient = new TelHTTPTSPClient();
  try
  {
    tspClient.HTTPClient = httpClient;
    tspClient.URL = "http://tsa.myserver.com";
    httpClient.SocketTimeout = 20000; // 20 seconds

    // Adding the signature time stamp
    int k = XAdESSigner/XAdESVerifier.AddSignatureTimestamp(tspClient);
    if (k != 0)
      throw new Exception("Failed to time-stamp: " + k.ToString());
  }
  finally
  {
    // Dispose the components
    tspClient.Dispose();
    httpClient.Dispose();
  }

Note: If the AddSignatureTimestamp method is called in [XAdES PLACE #1] it will always return 0 (success), but the actual time-stamping will be performed in the Signer.SaveEnveloped/SaveEnveloping/SaveDetached/Save method after signing. Depending on the XAdESSigner.IgnoreTimestampFailure property, if an error occurs while time-stamping in the Signer.Save* method, it would be ignored or an exception would be raised/thrown. Also, in this case, the tspClient and httpClient objects shouldn't be freed/disposed before the Signer.Save* method.


Creating Electronic Signatures with Complete Validation Data References (XAdES-C)

The XAdES-C form extends the XAdES-T or lower forms with the complete certificates and revocation information references. It allows the verifier to reconstruct the PKI environment in the revision it used to be in at the moment of signing. This form is useful for those situations where such information is archived by an external source, like a trusted service provider. Only the inclusion of the references helps to reduce the signature size.

The XAdES components can automatically collect certificates and revocation information or use custom certificates and revocation information.

Automatic collection in [XAdES PLACE #1] is done by calling the XAdESSigner.Generate method with parameter XAdES_C or higher.

Automatic collection in [XAdES PLACE #2] and [XAdES PLACE #3] is done by calling the AddValidationDataRefs method:

[Delphi]

  Validity := XAdESSigner/XAdESVerifier.AddValidationDataRefs();
  if (Validity <> xsvValid) and not XAdESVerifier.IgnoreChainValidationErrors then
    raise Exception.Create('Failed to add validation data references');

[C#]

  TSBXAdESValidity Validity = XAdESVerifier.AddValidationDataRefs();
  if ((Validity != TSBXAdESValidity.xsvValid) && !XAdESVerifier.IgnoreChainValidationErrors)
    throw new Exception("Failed to add validation data references");

The TElXAdESSigner/TElXAdESVerifier components perform automatic collection of certificate and revocation information by internally validating the signing certificate with the use of the TElX509CertificateValidator component. To get access to this component and a collection of its tracing events or to fine-tune the component, handle the OnBeforeCertificateValidate event or assign the TElX509CertificateValidator instance to the CertificateValidator property.

Custom certificates and revocation information can be added in [XAdES PLACE #2] and [XAdES PLACE #3] using the AddCompleteCertificateRefs and AddCompleteRevocationRefs methods.

[Delphi, C#]

XAdESSigner/XAdESVerifier.AddCompleteCertificateRefs(Certificates);
XAdESSigner/XAdESVerifier.AddCompleteRevocationRefs(CRLs, OCSPResponses);

In the preceding example, the Certificates object is a certificate storage that contains the certificates to add. The CRLs and OCSPResponses objects are CRL storage and OCSP response lists respectively.

To store certificates and revocation values for archival purposes, handle the OnStoreCertificate, OnStoreCRL, and OnStoreOCSPResponse events. To retrieve certificates and revocation values for validation purposes, handle the OnRetrieveCertificate, OnRetrieveCRL, and OnRetrieveOCSPResponse events.


Creating Extended Signatures with Time Indication Forms (XAdES-X)

The XAdES-X form extends the XAdES-C form with an external time stamp that certifies the references to the validation data or on the SignatureValue element, signature time stamps if present, and the aforementioned validation data. This time stamp takes into account the risk that any keys used in the certificate chain or in the revocation status information may be compromised. Depending on what is time-stamped, there are two different types of XAdES-X signatures, namely, XAdES-X type 1 and XAdES-X type 2. To be able to create XAdES-X signatures you must have access to an online TSA service that is capable of producing RFC3161-compliant time stamps.

[Delphi]

  // Creating the time-stamping components.
  HttpClient := TElHTTPSClient.Create(nil);
  // (!) Remember to handle the HttpClient's OnCertificateValidate
  // event if the TSA server is accessible via the HTTPS protocol.

  TspClient := TElHTTPTSPClient.Create(nil);
  try
    TspClient.HTTPClient := HttpClient;
    TspClient.URL := 'http://tsa.myserver.com';
    HttpClient.SocketTimeout := 20000; // 20 seconds

    // Adding the SigAndRefs time-stamp (XAdES-X type 1)
    k := XAdESSigner/XAdESVerifier.AddSigAndRefsTimestamp(TspClient);
    if k <> 0 then
      raise Exception.CreateFmt('Failed to time-stamp: %d', [k]);

    // Or adding the RefsOnly time-stamp (XAdES-X type 2)
    k := XAdESSigner/XAdESVerifier.AddRefsOnlyTimestamp(tspClient);
    if k <> 0 then
      raise Exception.CreateFmt('Failed to time-stamp: %d', [k]);

  finally
    // Free the components
    FreeAndNil(TspClient);
    FreeAndNil(HttpClient);
  end;

[C#]

  // Creating the time-stamping components
  httpClient = new TElHTTPSClient();
  // (!) Remember to handle the httpClient's OnCertificateValidate
  // event if the TSA server is accessible via the HTTPS protocol.

  tspClient = new TelHTTPTSPClient();
  try
  {
    tspClient.HTTPClient = httpClient;
    tspClient.URL = "http://tsa.myserver.com";
    httpClient.SocketTimeout = 20000; // 20 seconds

    // Adding the SigAndRefs time-stamp (XAdES-X type 1)
    int k = XAdESSigner/XAdESVerifier.AddSigAndRefsTimestamp(tspClient);
    if (k != 0)
      throw new Exception("Failed to time-stamp: " + k.ToString());

    // Or adding the RefsOnly time-stamp (XAdES-X type 2)
    k = XAdESSigner/XAdESVerifier.AddRefsOnlyTimestamp(tspClient);
    if (k != 0)
      throw new Exception("Failed to time-stamp: " + k.ToString());
  }
  finally
  {
    // Dispose the components
    tspClient.Dispose();
    httpClient.Dispose();
  }

Note: If the AddSigAndRefsTimestamp or AddRefsOnlyTimestamp methods are called in [XAdES PLACE #1], they will always return 0 (success), but the actual time-stamping would be performed in the Signer.SaveEnveloped/SaveEnveloping/SaveDetached/Save method after signing. Depending on the XAdESSigner.IgnoreTimestampFailure property, if an error occurs while time-stamping in the Signer.Save* method, it would be ignored or an exception would be raised/thrown. Also, in this case, the tspClient and httpClient objects shouldn't be freed/disposed before the Signer.Save* method.


Creating Extended Long Electronic Signatures with Time (XAdES-X-L)

The XAdES-X-L form extends the XAdES-X type 1 or XAdES-X type 2 or lower forms by inserting the certificate and revocation information values of the referenced validation data (certificates, CRLs, and OCSP responses) to the unsigned properties. The XAdES-X-L signature is thus a self-contained signature that does not require any external certificate or revocation provisioning services to be successfully validated. The XAdES components can automatically collect certificates and revocation information or use custom certificates and revocation information. Automatic collection in [XAdES PLACE #1] is done by calling the XAdESSigner.Generate method with parameter XAdES_X_L or higher. Automatic collection in [XAdES PLACE #2] and [XAdES PLACE #3] is done by calling the AddValidationDataValues method:

[Delphi]

  Validity := XAdESSigner/XAdESVerifier.AddValidationDataValues();
  if (Validity <> xsvValid) and not XAdESVerifier.IgnoreChainValidationErrors then
    raise Exception.Create('Failed to add validation data values');

[C#]

  TSBXAdESValidity Validity = XAdESVerifier.AddValidationDataValues();
  if ((Validity != TSBXAdESValidity.xsvValid) && !XAdESVerifier.IgnoreChainValidationErrors)
    throw new Exception("Failed to add validation data values");

The TElXAdESSigner/TElXAdESVerifier components perform the automatic collection of certificates and revocation information by internally validating the signing certificate with the use of the TElX509CertificateValidator component. To get access to this component and a collection of its tracing events or to fine-tune the component, handle the OnBeforeCertificateValidate event or assign the TElX509CertificateValidator instance to the CertificateValidator property.

Custom certificates and revocation information can be added in [XAdES PLACE #2] and [XAdES PLACE #3] using the AddCertificateValues and AddRevocationValues methods.

[Delphi, C#]

  XAdESSigner/XAdESVerifier.AddCertificateValues(Certificates);
  XAdESSigner/XAdESVerifier.AddRevocationValues(CRLs, OCSPResponses);

In the previous example, the Certificates object is a certificate storage that contains the certificates to add. The CRLs and OCSPResponses objects are CRL storage and OCSP response lists respectively.

To retrieve certificates and revocation values that were previously stored in a XAdES-C form (if the XAdES-X-L form is not generated immediately after the XAdES-C form), handle the OnRetrieveCertificate, OnRetrieveCRL, and OnRetrieveOCSPResponse events.


Creating Archival Electronic Signatures (XAdES-A)

The XAdES-A form adds additional time stamps for archiving signatures in a way that they are protected if the cryptographic data becomes weak. To be able to create XAdES-A signatures, you must have access to an online TSA service that is capable of producing RFC3161-compliant time stamps.

[Delphi]

  // Creating the time-stamping components.
  HttpClient := TElHTTPSClient.Create(nil);
  // (!) Remember to handle the HttpClient's OnCertificateValidate
  // event if the TSA server is accessed via the HTTPS protocol.

  TspClient := TElHTTPTSPClient.Create(nil);
  try
    TspClient.HTTPClient := HttpClient;
    TspClient.URL := "http://tsa.myserver.com";
    HttpClient.SocketTimeout := 20000; // 20 seconds

    // Adding an archival time-stamp
    k := XAdESSigner/XAdESVerifier.AddArchiveTimestamp/AddArchiveTimestampV141 (TspClient);
    if k <> 0 then
      raise Exception.CreateFmt('Failed to time-stamp: %d', [k]);

  finally
    // Free the components
    FreeAndNil(TspClient);
    FreeAndNil(HttpClient);
  end;

[C#]

  // Creating the time-stamping components.
  httpClient = new TElHTTPSClient();
  // (!) Remember to handle the httpClient's OnCertificateValidate
  // event if the TSA server is accessible via the HTTPS protocol.

  tspClient = new TelHTTPTSPClient();
  try
  {
    tspClient.HTTPClient = httpClient;
    tspClient.URL = "http://tsa.myserver.com";
    httpClient.SocketTimeout = 20000; // 20 seconds

    // Adding an archival time stamp
    int k = XAdESSigner/XAdESVerifier.AddArchiveTimestamp/ AddArchiveTimestampV141(tspClient);
    if (k != 0)
      throw new Exception("Failed to time-stamp: " + k.ToString());
  }
  finally
  {
    // Dispose the components
    tspClient.Dispose();
    httpClient.Dispose();
  }

Note: If the AddArchiveTimestamp or AddArchiveTimestampV141 methods are called in [XAdES PLACE #1], they will always return 0 (success), but the actual time-stamping will be performed in the Signer.SaveEnveloped/SaveEnveloping/SaveDetached/Save method after signing. Depending on the XAdESSigner.IgnoreTimestampFailure property, if an error occurs while time-stamping in the Signer.Save* method, it would be ignored or an exception would be raised/thrown. Also, in this case, the tspClient and httpClient objects shouldn't be freed/disposed before the Signer.Save* method.

Note: If the XAdES version is 1.4.1 then the AddArchiveTimestamp method will add an archival time stamp of version 1.4.1 (the equivalent to calling the AddArchiveTimestampV141 method).

Note:The AddArchiveTimestampV141 method can't be called for XAdES versions lower then 1.3.2.


Preparing and Fine-Tuning the TElXAdESSigner and TElXAdESVerifier Components

Each PKI environment is different, so you will probably need to fine-tune the TElXAdESSigner and TElXAdESVerifier objects to match your particular one. The table below gives a breakdown of the most important properties.

IgnoreChainValidationErrors Prevents the component 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 debugging or testing purposes to suppress validation exceptions.
OfflineMode Tells the component that no Internet connections should be attempted when validating the chain. Ensure that appropriate certificates and revocation elements are available to the component (either in the body of the signature or locally) when switching this property on; otherwise, you might come across chain validation issues.
TrustedCertificates Provides a separate list of trusted certificates for use during the validation process.
GracePeriod Sets a period of 'idle time' (in milliseconds) between the moment of creation of a XAdES-T signature and collection of validation information for upgrading to a higher form, to allow the revocation status update to propagate through the PKI environment up to revocation status responders.
ValidationMoment Sets the moment of time at which the signature should be validated. Leave intact to use the current time from the local computer.

Registering Revocation Retriever Factories

The TElXAdESSigner, TElXAdESVerifier, 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 the HTTP OCSP client
  SBHTTPOCSPClient.Unit.RegisterHTTPOCSPClientFactory();

  // Registering the HTTP CRL retriever
  SBHTTPCRL.Unit.RegisterHTTPCRLRetrieverFactory();

  // Registering the LDAP CRL retriever
  SBLDAPCRL.Unit.RegisterLDAPCRLRetrieverFactory();

  // Registering the 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

  • [XML-DSig]: XML digital signature processing rules and syntax
  • [XAdES]: Electronic Signatures and Infrastructures (ESI); XML Advanced Electronic Signatures (XAdES) ETSI TS 101 903 V1.1.1, V1.2.2, V1.3.2, V1.4.1, V1.4.2

We appreciate your feedback.  If you have any questions, comments, or suggestions about this article please contact our support team at kb@nsoftware.com.