Skip to content
XML Digital Signatures — XML Signature Syntax, Signing, Verification, X.509, Canonicalization

XML Digital Signatures — XML Signature Syntax, Signing, Verification, X.509, Canonicalization

DodaTech Updated Jun 20, 2026 8 min read

XML Digital Signatures (XMLDSig) provide integrity, authentication, and non-repudiation for XML documents. This guide covers XML Signature structure, signing and verification in Java, X.509 certificate integration, canonicalization, and practical security considerations.

What You’ll Learn

You’ll understand XML Signature syntax (SignedInfo, SignatureValue, KeyInfo), sign XML documents programmatically in Java, verify signatures, integrate X.509 certificates, choose canonicalization algorithms, and avoid common security pitfalls. Durga Antivirus Pro uses XML signatures to verify the integrity of its threat signature updates.

Learning Path

    flowchart LR
  A[XQuery] --> B[XML Signatures<br/>You are here]
  B --> C[XSL-FO]
  C --> D[XML Security]
  style B fill:#f90,color:#fff
  

XML Signature Structure

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
        <CanonicalizationMethod
            Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
        <SignatureMethod
            Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
        <Reference URI="#data">
            <DigestMethod
                Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
            <DigestValue>Base64EncodedHash...</DigestValue>
        </Reference>
    </SignedInfo>
    <SignatureValue>Base64EncodedSignature...</SignatureValue>
    <KeyInfo>
        <X509Data>
            <X509Certificate>Base64Cert...</X509Certificate>
        </X509Data>
    </KeyInfo>
</Signature>

Signing XML in Java

import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.crypto.dsig.spec.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.*;

public class XmlSigner {
    private final PrivateKey privateKey;
    private final X509Certificate certificate;

    public XmlSigner(PrivateKey key, X509Certificate cert) {
        this.privateKey = key;
        this.certificate = cert;
    }

    public Document sign(Document document) throws Exception {
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

        // Create reference to the entire document
        Reference ref = fac.newReference(
            "",  // URI="" means sign the whole document
            fac.newDigestMethod(
                DigestMethod.SHA256, null),
            Collections.singletonList(
                fac.newCanonicalizationMethod(
                    CanonicalizationMethod.INCLUSIVE,
                    (C14NMethodParameterSpec) null)),
            null, null);

        // Create SignedInfo
        SignedInfo signedInfo = fac.newSignedInfo(
            fac.newCanonicalizationMethod(
                CanonicalizationMethod.INCLUSIVE, null),
            fac.newSignatureMethod(
                SignatureMethod.RSA_SHA256, null),
            Collections.singletonList(ref));

        // Create KeyInfo with X509 certificate
        KeyInfoFactory kif = fac.getKeyInfoFactory();
        X509Data x509Data = kif.newX509Data(
            Collections.singletonList(certificate));
        KeyInfo keyInfo = kif.newKeyInfo(
            Collections.singletonList(x509Data));

        // Sign the document
        DOMSignContext signContext = new DOMSignContext(
            privateKey, document.getDocumentElement());
        XMLSignature signature = fac.newXMLSignature(
            signedInfo, keyInfo);
        signature.sign(signContext);

        return document;
    }

    public static void main(String[] args) throws Exception {
        // Load private key and certificate
        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(new java.io.FileInputStream("keystore.jks"),
                "password".toCharArray());
        PrivateKey key = (PrivateKey) ks.getKey("mykey",
            "password".toCharArray());
        X509Certificate cert = (X509Certificate) ks.getCertificate("mykey");

        // Load XML document
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        Document doc = dbf.newDocumentBuilder()
            .parse(new java.io.File("document.xml"));

        // Sign
        XmlSigner signer = new XmlSigner(key, cert);
        Document signedDoc = signer.sign(doc);

        // Save signed document
        javax.xml.transform.TransformerFactory tf =
            javax.xml.transform.TransformerFactory.newInstance();
        javax.xml.transform.Transformer trans = tf.newTransformer();
        trans.transform(new javax.xml.transform.dom.DOMSource(signedDoc),
            new javax.xml.transform.stream.StreamResult(
                new java.io.File("signed-document.xml")));

        System.out.println("Document signed successfully.");
    }
}

Verification in Java

import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.keyinfo.*;
import java.security.cert.X509Certificate;

public class XmlVerifier {
    public boolean verify(Document document) throws Exception {
        // Find the Signature element
        NodeList sigList = document.getElementsByTagNameNS(
            XMLSignature.XMLNS, "Signature");

        if (sigList.getLength() == 0) {
            throw new Exception("No Signature element found");
        }

        Element sigElement = (Element) sigList.item(0);

        // Create validation context
        DOMValidateContext valContext = new DOMValidateContext(
            new X509KeySelector(), sigElement);

        // Unmarshal and validate
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
        XMLSignature signature = fac.unmarshalXMLSignature(valContext);

        boolean isValid = signature.validate(valContext);

        if (isValid) {
            System.out.println("Signature is VALID");
            // Verify the certificate chain
            X509Certificate cert = getCertificate(signature);
            cert.checkValidity();
        } else {
            System.out.println("Signature is INVALID");
            // Check validation status
            boolean sv = signature.getSignatureValue().validate(valContext);
            boolean dv = signature.getSignedInfo()
                .getReferences().get(0).validate(valContext);
            System.out.println("  Signature value valid: " + sv);
            System.out.println("  Digest valid: " + dv);
        }

        return isValid;
    }

    private X509Certificate getCertificate(XMLSignature signature) {
        KeyInfo ki = signature.getKeyInfo();
        for (Object o : ki.getContent()) {
            XMLStructure xs = (XMLStructure) o;
            if (xs instanceof X509Data) {
                X509Data xd = (X509Data) xs;
                for (Object item : xd.getContent()) {
                    if (item instanceof X509Certificate) {
                        return (X509Certificate) item;
                    }
                }
            }
        }
        return null;
    }

    static class X509KeySelector extends KeySelector {
        @Override
        public KeySelectorResult select(KeyInfo keyInfo, Purpose purpose,
                AlgorithmMethod method, XMLCryptoContext context)
                throws KeySelectorException {

            for (Object o : keyInfo.getContent()) {
                XMLStructure xs = (XMLStructure) o;
                if (xs instanceof X509Data) {
                    X509Data xd = (X509Data) xs;
                    for (Object item : xd.getContent()) {
                        if (item instanceof X509Certificate) {
                            final PublicKey key =
                                ((X509Certificate) item).getPublicKey();
                            return () -> key;
                        }
                    }
                }
            }
            throw new KeySelectorException("No X509 key found");
        }
    }
}

Canonicalization

XML canonicalization ensures that logically equivalent XML produces identical digests:

// Canonicalization algorithms
// 1. Inclusive (c14n): http://www.w3.org/TR/2001/REC-xml-c14n-20010315
// 2. Inclusive with comments: http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments
// 3. Exclusive (exc-c14n): http://www.w3.org/2001/10/xml-exc-c14n#
// 4. Exclusive with comments: http://www.w3.org/2001/10/xml-exc-c14n#WithComments

// Use exclusive canonicalization for signed elements in namespaced documents
SignedInfo signedInfo = fac.newSignedInfo(
    fac.newCanonicalizationMethod(
        CanonicalizationMethod.EXCLUSIVE, null),
    fac.newSignatureMethod(
        SignatureMethod.RSA_SHA256, null),
    refs);

Common XML Signature Mistakes

1. Not Canonicalizing Before Signing

Without canonicalization, whitespace differences, attribute ordering, and namespace declarations change the digest. Always canonicalize both during signing and verification.

2. Using SHA-1 or MD5

These hash algorithms are deprecated and insecure for digital signatures. Use SHA-256 (http://www.w3.org/2001/04/xmlenc#sha256) or stronger.

3. Not Validating the Certificate Chain

Verifying the signature value doesn’t verify the signer’s identity. Check that the X.509 certificate is valid, trusted, and not revoked. Use OCSP or CRL during verification.

4. Signing Without URI Reference Scope

An empty URI (URI="") signs the entire document. If you sign a specific element, ensure the URI reference correctly points to it using an ID attribute.

5. XML Signature Wrapping Attacks

Attackers can move a valid signature to a different context. Verify that the signed content matches what you expect by checking the signature’s position and the referenced element’s enclosing context.

6. Not Handling Enveloped vs Detached Signatures

Enveloped signature wraps the signed data. Detached signature is separate. Enveloping signature wraps the data inside Signature. Choose based on use case and handle accordingly during verification.

7. Ignoring Exclusive Canonicalization for Namespaces

Inclusive canonicalization includes ancestor namespace declarations in the digest, causing failures if the XML context changes. Use exclusive canonicalization (exc-c14n) for elements that may be extracted from their original context.

Practice Questions

1. What is the purpose of canonicalization in XML signatures? Canonicalization standardizes XML into a canonical form so that logically identical documents (differing only in whitespace, attribute order, or namespace prefixes) produce identical digests.

2. What are the three main parts of an XML Signature? SignedInfo (digest, canonicalization, signature algorithm), SignatureValue (the actual signature bytes), and KeyInfo (key material or certificate for verification).

3. How does an enveloped signature differ from a detached signature? Enveloped: signature wraps the signed data (or vice versa) — signature is part of the signed document. Detached: signature is separate from the signed data, useful when you can’t modify the source document.

4. Why should you use SHA-256 instead of SHA-1? SHA-1 has known collision attacks — an attacker can create two documents with the same SHA-1 hash but different content. SHA-256 is collision-resistant and is the minimum recommended hash for digital signatures.

5. Challenge: Design an XML signature scheme for a software update system where updates are distributed via CDN and must be verified client-side without network access to certificate authorities. Answer: Use enveloped signatures. Embed the signing certificate in KeyInfo/X509Data. Clients store the root CA certificate. Verify by: (1) validating the XML signature, (2) verifying the embedded certificate chain to the trusted root, (3) checking the certificate hasn’t expired. No network access needed — all trust anchors are local.

Mini Project: XML Signature Tool

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
import java.io.File;
import java.security.*;
import java.security.cert.X509Certificate;

public class XmlSignatureTool {
    public enum Operation { SIGN, VERIFY }

    public static void main(String[] args) throws Exception {
        if (args.length < 3) {
            System.out.println("Usage:");
            System.out.println("  Sign: java XmlSignatureTool sign input.xml output.xml");
            System.out.println("  Verify: java XmlSignatureTool verify input.xml");
            return;
        }

        Operation op = Operation.valueOf(args[0].toUpperCase());
        File inputFile = new File(args[1]);

        // Load document
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        Document doc = dbf.newDocumentBuilder().parse(inputFile);

        if (op == Operation.SIGN) {
            // Load keystore
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("keystore.jks"), "changeit".toCharArray());
            PrivateKey key = (PrivateKey) ks.getKey("signing-key", "changeit".toCharArray());
            X509Certificate cert = (X509Certificate) ks.getCertificate("signing-key");

            XmlSigner signer = new XmlSigner(key, cert);
            Document signed = signer.sign(doc);

            // Save
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer trans = tf.newTransformer();
            trans.setOutputProperty(OutputKeys.INDENT, "yes");
            trans.transform(new DOMSource(signed),
                new StreamResult(new File(args[2])));
            System.out.println("Signed: " + args[2]);

        } else if (op == Operation.VERIFY) {
            XmlVerifier verifier = new XmlVerifier();
            boolean valid = verifier.verify(doc);

            if (valid) {
                System.out.println("SIGNATURE VALID");
                System.out.println("Document: " + inputFile.getName());
            } else {
                System.out.println("SIGNATURE INVALID!");
                System.exit(1);
            }
        }
    }
}

FAQ

What’s the difference between XML Signature and XML Encryption?
XML Signature provides integrity and authentication (signing). XML Encryption provides confidentiality (encryption). They can be combined — sign then encrypt, or encrypt then sign.
Can I use XML Signature with JSON?
No — XML Signature is specific to XML syntax. For JSON, use JWS (JSON Web Signature) which follows a similar concept but is JSON-native.
How do I handle certificate revocation?
Use OCSP (Online Certificate Status Protocol) during verification to check if the certificate is revoked. Alternatively, use CRLs (Certificate Revocation Lists) updated regularly.
What key sizes are recommended for XML signatures?
RSA: 2048-bit minimum, 4096-bit recommended. DSA: deprecated. ECDSA: P-256 or P-384 for equivalent security with smaller keys.
Can I sign only part of an XML document?
Yes — use a URI reference pointing to a specific element by ID. Set URI="#data" and add ID="data" to the element being signed.
How do I protect against replay attacks?
Include a timestamp or nonce in the signed data. The XML Signature itself doesn’t prevent replay — add <ds:Object> containing a timestamp or sequence number within the signed scope.

What’s Next

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-20.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro