XML Digital Signatures — XML Signature Syntax, Signing, Verification, X.509, Canonicalization
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 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