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
| Element | Purpose |
|---|---|
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:Objectof the signature element. The RSA signature covers onlySignedInfo, and the enveloped-signature transform excludes the whole signature element from the references. That's why the .NETCheckSignature(xmlRsa)confirms the signature as usual. - Return values like .NET.
CheckSignaturereturnsfalseif a signature doesn't match, including when the ML-DSA signature is missing inHybridmode. It throws aCryptographicExceptiononly 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.
Related topics
en/wiki/pqcrypt/lib/how-to/sign-xml-documents.txt · Last modified: by Wolfgang van der Stille