This library now supports the Web Crypto API, which allows it to run in browsers and modern Node.js environments without any Node.js-specific dependencies for cryptographic operations.
The WebCrypto implementation provides:
- Browser compatibility: Run XML signing and verification in the browser
- No Node.js crypto dependency: Uses the standard Web Crypto API
- Callback-based async operations: WebCrypto operations use callbacks for async handling
- Same API structure: Uses the same methods as Node.js crypto, just with callbacks
WebCryptoSha1- SHA-1 hashingWebCryptoSha256- SHA-256 hashingWebCryptoSha512- SHA-512 hashing
WebCryptoRsaSha1- RSA-SHA1 signing/verificationWebCryptoRsaSha256- RSA-SHA256 signing/verificationWebCryptoRsaSha512- RSA-SHA512 signing/verificationWebCryptoHmacSha1- HMAC-SHA1 signing/verification
import { SignedXml, WebCryptoRsaSha256, WebCryptoSha256 } from "xml-crypto";
// Your XML to sign
const xml = "<root><data>Hello World</data></root>";
// Your private key (PEM format)
const privateKey = `-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----`;
// Create SignedXml instance
const sig = new SignedXml();
// Use WebCrypto algorithms
sig.signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
sig.privateKey = privateKey;
// Add reference with WebCrypto hash algorithm
sig.addReference({
xpath: "//*[local-name(.)='data']",
digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256",
transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
});
// Register WebCrypto algorithms
sig.HashAlgorithms["http://www.w3.org/2001/04/xmlenc#sha256"] = WebCryptoSha256;
sig.SignatureAlgorithms["http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"] = WebCryptoRsaSha256;
// Compute signature with callback
sig.computeSignature(xml, (err, signedXmlObj) => {
if (err) {
console.error("Signing failed:", err);
return;
}
console.log(signedXmlObj.getSignedXml());
});import { SignedXml, WebCryptoRsaSha256, WebCryptoSha256 } from "xml-crypto";
import { DOMParser } from "@xmldom/xmldom";
const signedXml = `<root>...</root>`; // Your signed XML
const sig = new SignedXml();
// Register WebCrypto algorithms
sig.HashAlgorithms["http://www.w3.org/2001/04/xmlenc#sha256"] = WebCryptoSha256;
sig.SignatureAlgorithms["http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"] = WebCryptoRsaSha256;
// Provide public key or certificate (SPKI format)
sig.publicCert = publicKey;
// Load the signature - need to extract it from the signed XML first
const doc = new DOMParser().parseFromString(signedXml);
const signature = sig.findSignatures(doc)[0];
sig.loadSignature(signature);
// Verify with callback
sig.checkSignature(signedXml, (err, isValid) => {
if (err) {
console.error("Verification failed:", err);
return;
}
console.log("Signature valid:", isValid);
});The WebCrypto algorithms accept keys in PEM format (strings) and will automatically convert them to CryptoKey objects internally.
Note: For verification, WebCrypto requires public keys in SPKI format, not X.509 certificates. See the X.509 Certificates section below for how to extract the public key.
Both computeSignature and checkSignature support an optional callback parameter. When using WebCrypto algorithms, you must provide a callback to handle the asynchronous operations:
// Signing with callback
sig.computeSignature(xml, (err, signedXmlObj) => {
if (err) {
console.error("Error:", err);
return;
}
// signedXmlObj is the SignedXml instance
const result = signedXmlObj.getSignedXml();
});
// Verification with callback
sig.checkSignature(signedXml, (err, isValid) => {
if (err) {
console.error("Error:", err);
return;
}
console.log("Valid:", isValid);
});Important: If you try to use WebCrypto algorithms without providing a callback, the operation will fail because WebCrypto operations are inherently asynchronous.
The WebCrypto API is supported in all modern browsers:
- Chrome/Edge 37+
- Firefox 34+
- Safari 11+
WebCrypto is available in Node.js 15.0.0+ via the global crypto.subtle object. For older Node.js versions, continue using the standard crypto-based algorithms.
To migrate from Node.js crypto to WebCrypto:
-
Change algorithm imports:
// Before import { Sha256, RsaSha256 } from "xml-crypto"; // After import { WebCryptoSha256, WebCryptoRsaSha256 } from "xml-crypto";
-
Update to use callbacks:
// Before (synchronous) sig.computeSignature(xml); const result = sig.getSignedXml(); // After (with callback) sig.computeSignature(xml, (err, signedXmlObj) => { if (err) { console.error(err); return; } const result = signedXmlObj.getSignedXml(); });
-
Register algorithms:
sig.HashAlgorithms["http://www.w3.org/2001/04/xmlenc#sha256"] = WebCryptoSha256; sig.SignatureAlgorithms["http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"] = WebCryptoRsaSha256;
-
X.509 Certificates: The Web Crypto API doesn't directly support X.509 certificates. If you have a certificate, you need to extract the public key in SPKI format first:
// In Node.js, you can extract it like this: import { createPublicKey } from "crypto"; const publicKey = createPublicKey(certificatePem); const spkiPublicKey = publicKey.export({ type: "spki", format: "pem", }); // Use spkiPublicKey with WebCrypto algorithms sig.publicCert = spkiPublicKey;
In browsers, you'll need to prepare the keys in SPKI format beforehand or use a library to parse X.509 certificates.
-
PEM/DER parsing: The utility functions provide basic PEM parsing.
-
Key formats: Only PKCS8 private keys and SPKI public keys are currently supported for RSA.
-
Callback requirement: All WebCrypto operations require callbacks - you cannot use them with the synchronous API (without a callback).
- Zero dependencies on Node.js crypto: Run in any environment that supports Web Crypto API
- Browser support: Enable XML signing/verification in web applications
- Standard API: Uses the widely-supported Web Crypto API standard
- Future-proof: Web Crypto is the modern standard for cryptography on the web
import { SignedXml, WebCryptoRsaSha256, WebCryptoSha256 } from "xml-crypto";
import { DOMParser } from "@xmldom/xmldom";
function signAndVerify(callback) {
const xml = "<root><data>Important data</data></root>";
// Signing
const sigForSigning = new SignedXml();
sigForSigning.signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
sigForSigning.privateKey = privateKeyPem;
sigForSigning.addReference({
xpath: "//*[local-name(.)='data']",
digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256",
transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
});
// Register algorithms
sigForSigning.HashAlgorithms["http://www.w3.org/2001/04/xmlenc#sha256"] = WebCryptoSha256;
sigForSigning.SignatureAlgorithms["http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"] =
WebCryptoRsaSha256;
sigForSigning.computeSignature(xml, (err, signedXmlObj) => {
if (err) {
return callback(err);
}
const signedXml = signedXmlObj.getSignedXml();
// Verification
const sigForVerifying = new SignedXml();
sigForVerifying.publicCert = publicKeyPem; // SPKI format
// Register algorithms for verification
sigForVerifying.HashAlgorithms["http://www.w3.org/2001/04/xmlenc#sha256"] = WebCryptoSha256;
sigForVerifying.SignatureAlgorithms["http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"] =
WebCryptoRsaSha256;
// Load the signature
const doc = new DOMParser().parseFromString(signedXml);
const signature = sigForVerifying.findSignatures(doc)[0];
sigForVerifying.loadSignature(signature);
sigForVerifying.checkSignature(signedXml, (err, isValid) => {
if (err) {
return callback(err);
}
console.log("Signature is valid:", isValid);
callback(null, isValid);
});
});
}
signAndVerify((err, isValid) => {
if (err) {
console.error("Error:", err);
} else {
console.log("Success! Valid:", isValid);
}
});