Sample code for 30+ languages & platforms
PureBasic

XAdES using TSA Requiring Client Certificate

See more XML Digital Signatures Examples

Demonstrates how to create an XMLDSig (XAdES) signed document which includes an EncapsulatedTimestamp using a TSA (TimeStamp Authority) server requiring client certificate authentication. One such TSA is https://www3.postsignum.cz/TSS/TSS_crt/

Chilkat PureBasic Downloads

PureBasic
IncludeFile "CkJsonObject.pb"
IncludeFile "CkHttp.pb"
IncludeFile "CkXml.pb"
IncludeFile "CkXmlDSigGen.pb"
IncludeFile "CkStringBuilder.pb"
IncludeFile "CkCert.pb"

Procedure ChilkatExample()

    success.i = 0

    ; This example requires the Chilkat API to have been previously unlocked.
    ; See Global Unlock Sample for sample code.

    success = 1

    ; Load the XML to be signed.  For example, the XML to be signed might contain something like this:

    ; <?xml version="1.0" encoding="utf-8"?>
    ; <TransakcniLogSystemu xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://nsess.public.cz/erms_trans/v_01_01" Id="Signature1">
    ;   <TransLogInfo>
    ;     <Identifikator>XYZ ABC</Identifikator>
    ;     <DatumVzniku>2022-12-20T14:39:02.3625922+01:00</DatumVzniku>
    ;     <DatumCasOd>2022-12-20T14:26:26.88</DatumCasOd>
    ;     <DatumCasDo>2022-12-20T14:39:02.287</DatumCasDo>
    ;     <Software>XYZ</Software>
    ;     <VerzeSoftware>2.0.19.32</VerzeSoftware>
    ;   </TransLogInfo>
    ;   <Udalosti>
    ;     <Udalost>
    ;       <Poradi>1</Poradi>
    ; ...

    ; Load the XML to be signed from a file.
    ; (XML can be loaded from other source, such as a string variable.)
    sbXml.i = CkStringBuilder::ckCreate()
    If sbXml.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkStringBuilder::ckLoadFile(sbXml,"xmlToSign.xml","utf-8")

    gen.i = CkXmlDSigGen::ckCreate()
    If gen.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    CkXmlDSigGen::setCkSigLocation(gen, "TransakcniLogSystemu")
    CkXmlDSigGen::setCkSigLocationMod(gen, 0)
    CkXmlDSigGen::setCkSigId(gen, "SignatureID-Signature1")
    CkXmlDSigGen::setCkSigNamespacePrefix(gen, "ds")
    CkXmlDSigGen::setCkSigNamespaceUri(gen, "http://www.w3.org/2000/09/xmldsig#")
    CkXmlDSigGen::setCkSignedInfoCanonAlg(gen, "C14N")
    CkXmlDSigGen::setCkSignedInfoDigestMethod(gen, "sha256")

    ; Set the KeyInfoId before adding references..
    CkXmlDSigGen::setCkKeyInfoId(gen, "KeyInfoId-Signature-Signature1")

    ; Create an Object to be added to the Signature.

    ; Note: Chilkat will automatically fill in the values marked as "TO BE GENERATED BY CHILKAT" at the time of signing.
    ; The EncapsulatedTimestamp will be automatically generated.

    object1.i = CkXml::ckCreate()
    If object1.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    CkXml::setCkTag(object1, "xades:QualifyingProperties")
    CkXml::ckAddAttribute(object1,"xmlns:xades","http://uri.etsi.org/01903/v1.3.2#")
    CkXml::ckAddAttribute(object1,"Target","#Signature1")

    CkXml::ckUpdateAttrAt(object1,"xades:SignedProperties",1,"Id","SignedProperties-Signature-Signature1")
    CkXml::ckUpdateChildContent(object1,"xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningTime","TO BE GENERATED BY CHILKAT")
    CkXml::ckUpdateAttrAt(object1,"xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificateV2|xades:Cert|xades:CertDigest|ds:DigestMethod",1,"Algorithm","http://www.w3.org/2001/04/xmlenc#sha256")
    CkXml::ckUpdateChildContent(object1,"xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificateV2|xades:Cert|xades:CertDigest|ds:DigestValue","TO BE GENERATED BY CHILKAT")
    CkXml::ckUpdateChildContent(object1,"xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificateV2|xades:Cert|xades:IssuerSerialV2","TO BE GENERATED BY CHILKAT")

    ; The EncapsulatedTimestamp will be included in the unsigned properties.
    CkXml::ckUpdateAttrAt(object1,"xades:UnsignedProperties|xades:UnsignedSignatureProperties|xades:SignatureTimeStamp",1,"Id","signature-timestamp-5561-8212-3316-5191")
    CkXml::ckUpdateAttrAt(object1,"xades:UnsignedProperties|xades:UnsignedSignatureProperties|xades:SignatureTimeStamp|ds:CanonicalizationMethod",1,"Algorithm","http://www.w3.org/2001/10/xml-exc-c14n#")
    CkXml::ckUpdateAttrAt(object1,"xades:UnsignedProperties|xades:UnsignedSignatureProperties|xades:SignatureTimeStamp|xades:EncapsulatedTimeStamp",1,"Encoding","http://uri.etsi.org/01903/v1.2.2#DER")
    CkXml::ckUpdateChildContent(object1,"xades:UnsignedProperties|xades:UnsignedSignatureProperties|xades:SignatureTimeStamp|xades:EncapsulatedTimeStamp","TO BE GENERATED BY CHILKAT")

    CkXmlDSigGen::ckAddObject(gen,"XadesObjectId-Signature1",CkXml::ckGetXml(object1),"","")

    ; -------- Reference 1 --------
    CkXmlDSigGen::ckAddObjectRef(gen,"SignedProperties-Signature-Signature1","sha256","EXCL_C14N","","http://uri.etsi.org/01903#SignedProperties")

    ; -------- Reference 2 --------
    CkXmlDSigGen::ckAddSameDocRef(gen,"KeyInfoId-Signature-Signature1","sha256","EXCL_C14N","","")
    CkXmlDSigGen::ckSetRefIdAttr(gen,"KeyInfoId-Signature-Signature1","ReferenceKeyInfo")

    ; -------- Reference 3 --------
    CkXmlDSigGen::ckAddSameDocRef(gen,"","sha256","EXCL_C14N","","")
    CkXmlDSigGen::ckSetRefIdAttr(gen,"","Reference-Signature1")

    ; Provide a certificate + private key. (PFX password is test123)
    cert.i = CkCert::ckCreate()
    If cert.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkCert::ckLoadPfxFile(cert,"qa_data/pfx/cert_test123.pfx","test123")
    If success <> 1
        Debug CkCert::ckLastErrorText(cert)
        CkStringBuilder::ckDispose(sbXml)
        CkXmlDSigGen::ckDispose(gen)
        CkXml::ckDispose(object1)
        CkCert::ckDispose(cert)
        ProcedureReturn
    EndIf

    CkXmlDSigGen::ckSetX509Cert(gen,cert,1)

    CkXmlDSigGen::setCkKeyInfoType(gen, "X509Data")
    CkXmlDSigGen::setCkX509Type(gen, "Certificate")

    CkXmlDSigGen::setCkBehaviors(gen, "IndentedSignature")

    ; -------------------------------------------------------------------------------------------
    ; To have the EncapsulatedTimeStamp automatically added... 
    ; 1) Add the <xades:EncapsulatedTimeStamp Encoding="http://uri.etsi.org/01903/v1.2.2#DER">TO BE GENERATED BY CHILKAT</xades:EncapsulatedTimeStamp>
    ;    to the unsigned properties.  (This was accomplished in the above code.)
    ; 2) Specify the TSA URL (Timestamping Authority URL).
    ;    Here we specify the TSA URL:
    ; -------------------------------------------------------------------------------------------

    jsonTsa.i = CkJsonObject::ckCreate()
    If jsonTsa.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    CkJsonObject::ckUpdateString(jsonTsa,"timestampToken.tsaUrl","https://www3.postsignum.cz/TSS/TSS_crt/")
    CkJsonObject::ckUpdateBool(jsonTsa,"timestampToken.requestTsaCert",1)
    CkXmlDSigGen::ckSetTsa(gen,jsonTsa)

    ; -------------------------------------------------------------------------------------------
    ; In this case, the TSA requires client certificate authentication.
    ; To provide your client certificate, the application will instantiate a Chilkat HTTP object,
    ; then set it up with a SSL/TLS client certificate, and then tell the XmlDSigGen object
    ; to use the HTTP object for connections to the TSA server.
    ; -------------------------------------------------------------------------------------------
    http.i = CkHttp::ckCreate()
    If http.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkHttp::ckSetSslClientCertPfx(http,"/home/bob/pfxFiles/myClientSideCertWithPrivateKey.pfx","pfxPassword")
    If success <> 1
        Debug CkHttp::ckLastErrorText(http)
        CkStringBuilder::ckDispose(sbXml)
        CkXmlDSigGen::ckDispose(gen)
        CkXml::ckDispose(object1)
        CkCert::ckDispose(cert)
        CkJsonObject::ckDispose(jsonTsa)
        CkHttp::ckDispose(http)
        ProcedureReturn
    EndIf

    ; Tell the XmlDSigGen object to use the above HTTP object for TSA communications.
    CkXmlDSigGen::ckSetHttpObj(gen,http)

    ; Sign the XML...
    success = CkXmlDSigGen::ckCreateXmlDSigSb(gen,sbXml)
    If success <> 1
        Debug CkXmlDSigGen::ckLastErrorText(gen)
        CkStringBuilder::ckDispose(sbXml)
        CkXmlDSigGen::ckDispose(gen)
        CkXml::ckDispose(object1)
        CkCert::ckDispose(cert)
        CkJsonObject::ckDispose(jsonTsa)
        CkHttp::ckDispose(http)
        ProcedureReturn
    EndIf

    ; -----------------------------------------------

    ; Save the signed XML to a file.
    success = CkStringBuilder::ckWriteFile(sbXml,"c:/temp/qa_output/signedXml.xml","utf-8",0)

    Debug CkStringBuilder::ckGetAsString(sbXml)


    CkStringBuilder::ckDispose(sbXml)
    CkXmlDSigGen::ckDispose(gen)
    CkXml::ckDispose(object1)
    CkCert::ckDispose(cert)
    CkJsonObject::ckDispose(jsonTsa)
    CkHttp::ckDispose(http)


    ProcedureReturn
EndProcedure