Sample code for 30+ languages & platforms
Zig

Create XAdES-T Signed XML

See more XAdES Examples

This example signs XML using the XAdES-T profile. XAdES-T is a profile within the XAdES standard that adds support for secure timestamping of signatures.

Secure timestamping involves adding a timestamp to the signature, indicating the exact time when the signature was applied.

Timestamping enhances the long-term validity of signatures by providing evidence that the signature existed at a specific point in time, even if the signer's certificate has expired or been revoked.

XAdES-T signatures include elements for embedding timestamp data within the XML signature, along with information about the timestamp authority and the timestamp verification process.

XAdES-T signatures are suitable for scenarios where long-term validity and integrity of signatures are essential, such as in legal and regulatory contexts where archived documents may need to be validated years or decades later.

Chilkat Zig Downloads

Zig
const std = @import("std");
const chilkat = @import("chilkat");

pub fn main(init: std.process.Init) !void {
    const alloc = init.arena.allocator();

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

    // Create the XML to be signed...

    // Use this online tool to generate code from sample XML:
    // Generate Code to Create XML

    // <?xml version="1.0" encoding="UTF-8"?>
    // <es:Dossier xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns="http://uri.etsi.org/01903/v1.3.2#" xmlns:es="https://www.microsec.hu/ds/e-szigno30#" xsi:schemaLocation="https://www.microsec.hu/ds/e-szigno30# https://www.microsec.hu/ds/e-szigno30.xsd">
    //     <es:DossierProfile Id="PObject0" OBJREF="Object0">
    //     <es:Title>e-akta.es3</es:Title>
    //     <es:E-category>electronic dossier</es:E-category>
    //     <es:CreationDate>2022-12-02T07:55:16Z</es:CreationDate>
    //     </es:DossierProfile>
    //     <es:Documents Id="Object0"/>
    // </es:Dossier>

    const xml_to_sign = try chilkat.Xml.init();
    defer xml_to_sign.deinit();
    xml_to_sign.setTag("es:Dossier");
    xml_to_sign.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") catch {};
    xml_to_sign.addAttribute("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#") catch {};
    xml_to_sign.addAttribute("xmlns", "http://uri.etsi.org/01903/v1.3.2#") catch {};
    xml_to_sign.addAttribute("xmlns:es", "https://www.microsec.hu/ds/e-szigno30#") catch {};
    xml_to_sign.addAttribute("xsi:schemaLocation", "https://www.microsec.hu/ds/e-szigno30# https://www.microsec.hu/ds/e-szigno30.xsd") catch {};
    xml_to_sign.updateAttrAt("es:DossierProfile", true, "Id", "PObject0") catch {};
    xml_to_sign.updateAttrAt("es:DossierProfile", true, "OBJREF", "Object0") catch {};
    xml_to_sign.updateChildContent("es:DossierProfile|es:Title", "e-akta.es3");
    xml_to_sign.updateChildContent("es:DossierProfile|es:E-category", "electronic dossier");
    xml_to_sign.updateChildContent("es:DossierProfile|es:CreationDate", "2022-12-02T07:55:16Z");
    xml_to_sign.updateAttrAt("es:Documents", true, "Id", "Object0") catch {};

    const gen = try chilkat.XmlDSigGen.init();
    defer gen.deinit();

    gen.setSigLocation("es:Dossier");
    gen.setSigLocationMod(0);
    gen.setSigId("S9fe8096e-2cac-415d-9222-f6cf2ecb314b");
    gen.setSigValueId("VS9fe8096e-2cac-415d-9222-f6cf2ecb314b");
    gen.setSignedInfoId("SIS9fe8096e-2cac-415d-9222-f6cf2ecb314b");
    gen.setSignedInfoCanonAlg("EXCL_C14N");
    gen.setSignedInfoDigestMethod("sha256");

    // Set the KeyInfoId before adding references..
    gen.setKeyInfoId("KS9fe8096e-2cac-415d-9222-f6cf2ecb314b");

    // Create an Object to be added to the Signature.
    const object1 = try chilkat.Xml.init();
    defer object1.deinit();
    object1.setTag("es:SignatureProfile");
    object1.addAttribute("Id", "PS9fe8096e-2cac-415d-9222-f6cf2ecb314b") catch {};
    object1.addAttribute("OBJREF", "Object0") catch {};
    object1.addAttribute("SIGREF", "S9fe8096e-2cac-415d-9222-f6cf2ecb314b") catch {};
    object1.addAttribute("SIGREFLIST", "#Object0 #PS9fe8096e-2cac-415d-9222-f6cf2ecb314b #PObject0 #XS9fe8096e-2cac-415d-9222-f6cf2ecb314b") catch {};
    object1.updateChildContent("es:SignerName", "EC Minősített-Tesztelő Péterke");
    object1.updateChildContent("es:SDPresented", "false");
    object1.updateChildContent("es:Type", "signature");
    object1.updateAttrAt("es:Generator|es:Program", true, "name", "e-Szigno") catch {};
    object1.updateAttrAt("es:Generator|es:Program", true, "version", "3.3.6.8") catch {};
    object1.updateAttrAt("es:Generator|es:Device", true, "name", "OpenSSL 1.1.1n  15 Mar 2022") catch {};
    object1.updateAttrAt("es:Generator|es:Device", true, "type", "") catch {};

    gen.addObject("O1S9fe8096e-2cac-415d-9222-f6cf2ecb314b", try object1.getXml(alloc), "", "") catch {};

    // Create an Object to be added to the Signature.
    const object2 = try chilkat.Xml.init();
    defer object2.deinit();
    object2.setTag("QualifyingProperties");
    object2.addAttribute("Target", "#S9fe8096e-2cac-415d-9222-f6cf2ecb314b") catch {};
    object2.addAttribute("Id", "QPS9fe8096e-2cac-415d-9222-f6cf2ecb314b") catch {};
    object2.updateAttrAt("SignedProperties", true, "Id", "XS9fe8096e-2cac-415d-9222-f6cf2ecb314b") catch {};
    object2.updateChildContent("SignedProperties|SignedSignatureProperties|SigningTime", "TO BE GENERATED BY CHILKAT");
    object2.updateAttrAt("SignedProperties|SignedSignatureProperties|SigningCertificateV2|Cert|CertDigest|ds:DigestMethod", true, "Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256") catch {};
    object2.updateChildContent("SignedProperties|SignedSignatureProperties|SigningCertificateV2|Cert|CertDigest|ds:DigestValue", "TO BE GENERATED BY CHILKAT");
    object2.updateChildContent("SignedProperties|SignedSignatureProperties|SigningCertificateV2|Cert|IssuerSerialV2", "TO BE GENERATED BY CHILKAT");
    object2.updateChildContent("SignedProperties|SignedSignatureProperties|SignaturePolicyIdentifier|SignaturePolicyImplied", "");
    object2.updateChildContent("SignedProperties|SignedSignatureProperties|SignerRoleV2|ClaimedRoles|ClaimedRole", "tesztelő");

    // Here we have the EncapsulatedTimestamp found in the unsigned signature properties.
    object2.updateAttrAt("UnsignedProperties|UnsignedSignatureProperties|SignatureTimeStamp", true, "Id", "T72cb4961-4326-4319-857a-7cf55e7ef899") catch {};
    object2.updateAttrAt("UnsignedProperties|UnsignedSignatureProperties|SignatureTimeStamp|ds:CanonicalizationMethod", true, "Algorithm", "http://www.w3.org/2001/10/xml-exc-c14n#") catch {};
    object2.updateAttrAt("UnsignedProperties|UnsignedSignatureProperties|SignatureTimeStamp|EncapsulatedTimeStamp", true, "Id", "ET72cb4961-4326-4319-857a-7cf55e7ef899") catch {};
    object2.updateChildContent("UnsignedProperties|UnsignedSignatureProperties|SignatureTimeStamp|EncapsulatedTimeStamp", "TO BE GENERATED BY CHILKAT");
    object2.updateAttrAt("UnsignedProperties|UnsignedSignatureProperties|TimeStampValidationData", true, "xmlns", "http://uri.etsi.org/01903/v1.4.1#") catch {};
    object2.updateAttrAt("UnsignedProperties|UnsignedSignatureProperties|CertificateValues", true, "Id", "CV18c7702d-d45b-44bc-853a-a720f41053cd") catch {};
    object2.updateAttrAt("UnsignedProperties|UnsignedSignatureProperties|CertificateValues|EncapsulatedX509Certificate", true, "Id", "EC42db04c8-1422-407b-8c42-189353a55268") catch {};
    object2.updateChildContent("UnsignedProperties|UnsignedSignatureProperties|CertificateValues|EncapsulatedX509Certificate", "BASE64_CONTENT");
    object2.updateAttrAt("UnsignedProperties|UnsignedSignatureProperties|CertificateValues|EncapsulatedX509Certificate[1]", true, "Id", "EC04728b44-a32c-46c1-b9bb-85b1f6b3c7d3") catch {};
    object2.updateChildContent("UnsignedProperties|UnsignedSignatureProperties|CertificateValues|EncapsulatedX509Certificate[1]", "BASE64_CONTENT");

    gen.addObject("O2S9fe8096e-2cac-415d-9222-f6cf2ecb314b", try object2.getXml(alloc), "", "") catch {};

    // -------- Reference 1 --------
    gen.addSameDocRef("Object0", "sha256", "EXCL_C14N", "", "") catch {};
    gen.setRefIdAttr("Object0", "Re1f816c4-7898-4544-9b41-f4156dc0c528") catch {};

    // -------- Reference 2 --------
    gen.addObjectRef("PS9fe8096e-2cac-415d-9222-f6cf2ecb314b", "sha256", "EXCL_C14N", "", "") catch {};
    gen.setRefIdAttr("PS9fe8096e-2cac-415d-9222-f6cf2ecb314b", "Ra873b616-e568-4c38-ae94-27fbff67cc43") catch {};

    // -------- Reference 3 --------
    gen.addSameDocRef("PObject0", "sha256", "EXCL_C14N", "", "") catch {};
    gen.setRefIdAttr("PObject0", "Ra5d85948-5d6a-4914-8c32-242f5d6d9e81") catch {};

    // -------- Reference 4 --------
    gen.addObjectRef("XS9fe8096e-2cac-415d-9222-f6cf2ecb314b", "sha256", "EXCL_C14N", "", "http://uri.etsi.org/01903#SignedProperties") catch {};
    gen.setRefIdAttr("XS9fe8096e-2cac-415d-9222-f6cf2ecb314b", "Ra7412a43-dc05-4e0a-ac84-e9a070214757") catch {};

    // Provide a certificate + private key. (PFX password is test123)
    const cert = try chilkat.Cert.init();
    defer cert.deinit();
    cert.loadPfxFile("qa_data/pfx/cert_test123.pfx", "test123") catch {
        std.debug.print("{s}\n", .{try cert.getLastErrorText(alloc)});
        return;
    };

    gen.setX509Cert(cert, true) catch {};

    gen.setKeyInfoType("X509Data");
    gen.setX509Type("Certificate");

    // -------------------------------------------------------------------------------------------
    // To have the EncapsulatedTimeStamp automatically added, we only need to do 2 things.
    // 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.
    // 2) Specify the TSA URL (Timestamping Authority URL).
    //    Here we specify the TSA URL:
    // -------------------------------------------------------------------------------------------

    const json_tsa = try chilkat.JsonObject.init();
    defer json_tsa.deinit();
    json_tsa.updateString("timestampToken.tsaUrl", "http://timestamp.digicert.com") catch {};
    json_tsa.updateBool("timestampToken.requestTsaCert", true) catch {};
    gen.setTsa(json_tsa) catch {};

    // Load XML to be signed...
    const sb_xml = try chilkat.StringBuilder.init();
    defer sb_xml.deinit();
    xml_to_sign.getXmlSb(sb_xml) catch {};

    gen.setBehaviors("IndentedSignature,OmitAlreadyDefinedSigNamespace");

    // Sign the XML...
    gen.createXmlDSigSb(sb_xml) catch {
        std.debug.print("{s}\n", .{try gen.getLastErrorText(alloc)});
        return;
    };

    // -----------------------------------------------

    // Save the signed XML to a file.
    try sb_xml.writeFile("c:/temp/qa_output/signedXml.xml", "utf-8", false);

    std.debug.print("{s}\n", .{try sb_xml.getAsString(alloc)});

    // ----------------------------------------
    // Verify the signatures we just produced...
    const verifier = try chilkat.XmlDSig.init();
    defer verifier.deinit();
    verifier.loadSignatureSb(sb_xml) catch {
        std.debug.print("{s}\n", .{try verifier.getLastErrorText(alloc)});
        return;
    };

    const num_sigs = verifier.getNumSignatures();
    var verify_idx: i32 = 0;
    while (verify_idx < num_sigs) {
        verifier.setSelector(verify_idx);
        verifier.verifySignature(true) catch {
            std.debug.print("{s}\n", .{try verifier.getLastErrorText(alloc)});
            return;
        };

        verify_idx = verify_idx + 1;
    }

    std.debug.print("All signatures were successfully verified.\n", .{});
}