I was tasked to build an integration that would sign a xml using a x509Certificate, the solution was built using a Azure Function and creating a little Util class called Signed XMUtil.cs
In the handler load your xml and get the certificate from the keyvault (certificate)
Next call the sign util and finally log the signature if needed
var document = LoadXMLDocument(applicationRequest);
var certificate = await getX509CertificateByName(_keyVaultConfiguration.CertificateName);
_logger.LogDebug("Sign xml document using the certificate");
string result = SignXMLUtil.SignXMLDocument(document, certificate);
var signatureXML = GetSignatureElement(result); //get the signatur for logging purpose only, message is only base64 encoded and added to content
_logger.LogInformation("XML Signature:\r\n{XMLSignature}", signatureXML);
Here is the Sign xm util
/// <summary>
/// Sign xmlDocument using given x509Certificate
/// </summary>
internal static class SignXMLUtil
{
/// <summary>
/// Sign xmlDocument using provided x.509 certificate
/// </summary>
/// <param name="document"></param>
/// <param name="certificate"></param>
/// <returns>XML String with appended signature</returns>
/// <exception cref="InvalidOperationException"></exception>
internal static string SignXMLDocument(XmlDocument document, X509Certificate2 certificate)
{
try
{
// Create the SignedXml instance
SignedXml signedXml = new(document)
{
SigningKey = certificate.GetRSAPrivateKey()
};
// Create a reference to the entire document
Reference reference = new Reference()
{
Uri = ""
};
// Add an enveloped transformation to the reference
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigC14NTransform());
signedXml.AddReference(reference);
// Set the canonicalization method and signature method
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigCanonicalizationUrl;
signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; //digestmethod is set based on the signturemethod, this is handled automatically
// Add the KeyInfo with issuer and serial number
KeyInfoX509Data keyInfoX509Data = new(certificate);
keyInfoX509Data.AddIssuerSerial(certificate.Issuer, certificate.SerialNumber); //check if nordea have specific demand for this
signedXml.KeyInfo = new KeyInfo();
signedXml.KeyInfo.AddClause(keyInfoX509Data);
// Compute the signature.
signedXml.ComputeSignature();
XmlElement xmlDigitalSignature = signedXml.GetXml();
// Append the signature to the document
document
.DocumentElement?
.AppendChild(document.ImportNode(xmlDigitalSignature, true));
using (var stringWriter = new System.IO.StringWriter())
using (var xmlTextWriter = new XmlTextWriter(stringWriter))
{
document.WriteTo(xmlTextWriter);
return stringWriter.ToString(); //return signed xml untuched as created
}
}
catch (CryptographicException ex)
{
// Handle cryptographic exceptions appropriately
throw new InvalidOperationException("Error signing the XML document.", ex);
}
catch (Exception)
{
// Handle any other exceptions
throw;
}
}
}