Color scheme: Light
You are here: start » en » Documentation » Post-Quantum Cryptography » WKL.System.Security.Cryptography overview » How-to guides » Sign XML documents

Sign XML documents

You sign XML documents, such as electronic invoices, with the .NET SignedXml class, and you want to put an ML-DSA signature into the same signature element next to the RSA signature.

Solution overview

ElementPurpose
SignedXml.ComputeSignature(pqKey, CryptoMode.Hybrid)signs classically with SigningKey and adds an ML-DSA signature over the same SignedInfo
SignedXml.CheckSignature(key, pqPublicKey, CryptoMode.Hybrid)verifies both signatures
SignedXml.ComputeSignature(pqKey, CryptoMode.PostQuantum)signs with ML-DSA only; requires PostQuantumExemptions.PurePostQuantum

As usual with .NET, add the System.Security.Cryptography.Xml package to use SignedXml.

Implementation

Sign the document:

var document = new XmlDocument { PreserveWhitespace = true };
document.LoadXml("<Invoice><Number>2026-0042</Number><Amount currency=\"USD\">1190.00</Amount></Invoice>");
 
using var xmlRsa = RSA.Create(3072);
using var xmlPq = MLDsaKey.Generate(MLDsaParameterSet.MLDsa65);
 
var signedXml = new SignedXml(document) { SigningKey = xmlRsa };
var reference = new Reference(string.Empty);
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(reference);
signedXml.ComputeSignature(xmlPq, CryptoMode.Hybrid);
document.DocumentElement!.AppendChild(document.ImportNode(signedXml.GetXml(), true));
string signedDocument = document.OuterXml;

Verify the document at the recipient, which knows the RSA public key and the ML-DSA public key:

var received = new XmlDocument { PreserveWhitespace = true };
received.LoadXml(signedDocument);
var check = new SignedXml(received);
check.LoadXml((XmlElement)received.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl)[0]!);
 
Console.WriteLine(check.CheckSignature(xmlRsa, xmlPq, CryptoMode.Hybrid));

Remarks

  • Classical verifiers. The ML-DSA signature sits in a ds:Object of the signature element. The RSA signature covers only SignedInfo, and the enveloped-signature transform excludes the whole signature element from the references. That's why the .NET CheckSignature(xmlRsa) confirms the signature as usual.
  • Return values like .NET. CheckSignature returns false if a signature doesn't match, including when the ML-DSA signature is missing in Hybrid mode. It throws a CryptographicException only where .NET throws too, or when the PQ part can't be verified at all, for example without OpenSSL.
  • Preserve whitespace. Load documents with PreserveWhitespace = true. If the whitespace changes between signing and verification, neither signature matches anymore.
  • Provisional identifiers. The element, the namespace, and the algorithm URIs for ML-DSA are provisional; see Formats of the post-quantum parts.
en/wiki/pqcrypt/lib/how-to/sign-xml-documents.txt · Last modified: by Wolfgang van der Stille