I know there is a SignedXml class for signing XML document. However I am trying to calculate the signature value on my own, to understand what exactly is happening. More exactly, I am trying to sign a soap:Body element of SOAP message. I have manually crated the Signature tag so it matches the template. Also I have succesfully calculated the digest of and inserted this value into DigestValue tag. However I am not able to calculate correct value for the SigantureValue tag.
My approach is:
My code looks something like this:
// 1 Canonicalize the SignedInfo tag
XmlDsigExcC14NTransform serializer = new XmlDsigExcC14NTransform();
XmlDocument doc = new XmlDocument();
string toBeCanonicalized = signedInfoTag.OuterXml;
doc.LoadXml(toBeCanonicalized);
serializer.LoadInput(doc);
string c14n = new StreamReader((Stream)serializer.GetOutput(typeof(Stream))).ReadToEnd();
// 2 Hash the SignedInfo tag
SHA256 HashAlg = SHA256.Create();
byte[] hash = HashAlg.ComputeHash(Encoding.UTF8.GetBytes(c14n));
// 3 Sign the hash
byte[] signature;
using (RSACryptoServiceProvider csp = new RSACryptoServiceProvider())
{
csp.ImportParameters(((RSACryptoServiceProvider)mCertificate.PrivateKey).ExportParameters(true));
signature = csp.SignData(Encoding.UTF8.GetBytes(c14n), "SHA256");
}
signValueTag.InnerText = Convert.ToBase64String(signature);
What I am doing wrong?
The example of valid SOAP message is here:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1">
<wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="X509-A72D6FD4C41B1F545F14700558816386">MIID6zCCAtOgAwIBAgIEAQAAAzANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJDWjEaMBgGA1UEAwwRR0ZSIEVFVCB0ZXN0IENBIDExLTArBgNVBAoMJEdlbmVyw6FsbsOtIGZpbmFuxI1uw60gxZllZGl0ZWxzdHbDrTAeFw0xNjA1MTkxMjQ1MDJaFw0xODA1MTkxMjQ1MDJaMFMxCzAJBgNVBAYTAkNaMRUwEwYDVQQDDAxDWjEyMTIxMjEyMTgxFzAVBgNVBAoMDk9zb2JhIEZ5emlja8OhMRQwEgYDVQQFEwtUMDAwMDAwMDAwMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMt0eW9n+RB0PSawKSJbtAg3j7e1I5p7P9OvEj0n9raEMI496Zuw7s4VaE8JEX4iowjWhlPIOPljDiAXX6HgZzH4PDps0rFm388KZxj7Ek/ZLyyh5jRovc0Yccfgm3i2huBepk7ZtifZXOZzDEDT0CZsxRpypZJp9PK6SOdj2zPIc11F+prwsGQCDAZsRtama5/W5qn2YUWjjk+4c5Zu3TcknC7bcp1dg7RJ9yMtNiYPY7LNV3uWQhAXZVFpmOpbfYwT1F8H3/UrWDSJ5zrHshICPreMyZ0skU9SUANQ8QQKE6lSlgSs59YaeEmCyGtpttVjN+iR/L9M9FRtq3ZhZz0CAwEAAaOBwTCBvjAeBgNVHREEFzAVgRNlcG9kcG9yYUBmcy5tZmNyLmN6MB8GA1UdIwQYMBaAFHpa/A3L7DamDdppGWaMm++Cw6k0MB0GA1UdDgQWBBQbyTbuGBZdorOZZm7usMraGAJ2STBMBgNVHSAERTBDMEEGCmCGSAFlAwIBMAIwMzAxBggrBgEFBQcCAjAlGiNUZW50byBjZXJ0aWZpa2F0IEpFIFBPVVpFIFRFU1RPVkFDSTAOBgNVHQ8BAf8EBAMCBsAwDQYJKoZIhvcNAQELBQADggEBAC3L8Bm7ZlWui9xWrjM00SlvCokpc2ldGCxNvj4hANaISoLRdPVZAPeLd1X4KsRyxOIazR5oq3EKVZV1ZP3sCF4QFL+SqurkPiBbIrrbABDLyDpf/8DIfyA1x/+zNpN9ul9j9Ca1739P4L1x3wpQcYhEuvSrTiLztndlJb69LXgYZOFfqcBSRedRuMwRdtux9OWkkZjrd9wNHTCDOIfOPaPRRoq5IPHP32shsXaTLvhsT7ktvR5Fr/SQ8CkWq3U6tdcOQRN35ZWyYSyOd5/vkJqK773R/gAeBUE80gpLMdqgVj8HaD7RlHrYyYJEVUI9gbctTfUIJW/9LZ3K78JLXXg=</wsse:BinarySecurityToken>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-A72D6FD4C41B1F545F147005588163810">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soap"/>
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#id-A72D6FD4C41B1F545F14700558816389">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList=""/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>iiyihYsFMjO7QxIVCauydehAhjSm5LZlRGm3lT0VFY0=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>cGbhgNKCwrmUPXg2AKgqs1mceNcywK8BvrmmrOH627/3vadzKVnPiTn6ZaLBAcV1pYgTpNvh7RvAa8uZYXmS77YCQcYIOErbWKSTDVwBWv63d8fLm9Ljpx/1/PZrI7zSeIafXTLwPB2Lzt239ylZWPdhfg9XMhS43k4p7u1DZerVeRNSi76Q8u6jIWadDIQkn9mVEbhL5RIRGPoGJBof9QQVk42NHChdESW2RFXG7SSs2VYmdZ+IQUdEC7uPFoT/vxK2My1hGhYhvl6HNbMd5VIz/xMlDPrOCzbLWkA7oqyqSboTCObwkTwD2V20sxn6rb8mtak55zYaGXJldno66g==</ds:SignatureValue>
<ds:KeyInfo Id="KI-A72D6FD4C41B1F545F14700558816387">
<wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="STR-A72D6FD4C41B1F545F14700558816388">
<wsse:Reference URI="#X509-A72D6FD4C41B1F545F14700558816386" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
</SOAP-ENV:Header>
<soap:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-A72D6FD4C41B1F545F14700558816389">
<Trzba xmlns="http://fs.mfcr.cz/eet/schema/v3">
<Hlavicka dat_odesl="2016-09-19T19:06:37+02:00" prvni_zaslani="false" uuid_zpravy="f5ce1350-e688-4247-b1af-3d2bc592b83c"/>
<Data celk_trzba="34113.00" cerp_zuct="679.00" cest_sluz="5460.00" dan1="-172.39" dan2="-530.73" dan3="975.65" dat_trzby="2016-08-05T00:30:12+02:00" dic_popl="CZ1212121218" id_pokl="/5546/RO24" id_provoz="273" porad_cis="0/6460/ZQ42" pouzit_zboz1="784.00" pouzit_zboz2="967.00" pouzit_zboz3="189.00" rezim="0" urceno_cerp_zuct="324.00" zakl_dan1="-820.92" zakl_dan2="-3538.20" zakl_dan3="9756.46" zakl_nepodl_dph="3036.00"/>
<KontrolniKody>
<pkp cipher="RSA2048" digest="SHA256" encoding="base64">D84gY6RlfUi8dWdhL1zn0LE0s+aqLohtIxY0y88GoG5Ak8pBEH3/Ff2aFW7H6fvRxDMKsvM/VIYtUQxoDEctVGMSU/JDf9Vd0eQwgfLm683p316Sa4BUnVrIsHzwMyYkjpn66I072G2AvOUP4X5UiIYtHTwyMVyp+N/zzay3D7Q619ylDb6puN2iIlLsu+GNSB9DvsQbiLXPH6iK0R9FpR15v2y+0Uhh8NNJKl7O8Us9jbgokrA9gze+erQbhmwTm2nn2+7JGrPDqhyhwWZNLUziGSbC99wJpkEnIs0das/4hFNE3DnLvv4MsXwWCLOUZty6t6DAijlCzQj7KFKw0g==</pkp>
<bkp digest="SHA1" encoding="base16">8F8ABFEB-B76E7064-343A1460-6C6E6D86-B0F99C24</bkp>
</KontrolniKody>
</Trzba>
</soap:Body>
</soap:Envelope>
After banging my head against the wall trying to encrypt, hash, sign, with the certificate keys, I decided to check if there was some source code available for the SignedXml class. Thank God there is a reference source for .NET. So I went to the SignedXml.ComputeSignature method, to see how it worked. TL;DR, here is the code, based on those sources:
// Get signature description (using signature method algorithm http://www.w3.org/2001/04/xmldsig-more#rsa-sha256)
var signatureDescription
= System.Security.Cryptography.CryptoConfig.CreateFromName(System.Security.Cryptography.Xml.SignedXml.XmlDsigRSASHA256Url) as System.Security.Cryptography.SignatureDescription;
if (signatureDescription == null)
throw new System.Security.Cryptography.CryptographicException("SignatureDescriptionNotCreated");
// Get hash algorithm from signature description
System.Security.Cryptography.HashAlgorithm hashAlg = signatureDescription.CreateDigest();
if (hashAlg == null)
throw new System.Security.Cryptography.CryptographicException("CreateHashAlgorithmFailed");
// Load SignedInfo OuterXml in a separate XmlDocument, and canonicalize
var doc = new System.Xml.XmlDocument();
string toBeCanonicalized = signedInfoTag.OuterXml;
doc.LoadXml(toBeCanonicalized);
var transform = new System.Security.Cryptography.Xml.XmlDsigExcC14NTransform();
transform.LoadInput(doc);
byte[] hashvalue = transform.GetDigestedOutput(hashAlg); // hashvalue is not used, but I
// think this process allows to
// use hashAlg to get the
// SignatureValue later
// Create signature formatter using certificate's private key
System.Security.Cryptography.AsymmetricSignatureFormatter asymmetricSignatureFormatter
= signatureDescription.CreateFormatter(mCertificate.PrivateKey);
// Get signature value
byte[] signatureValueBytes = asymmetricSignatureFormatter.CreateSignature(hashAlg);
// Set signature value to node text, as Base64 string
signValueTag.InnerText = Convert.ToBase64String(signatureValueBytes)
Try to use
string toBeCanonicalized = signedInfoTag.InnerXml;
instead of
string toBeCanonicalized = signedInfoTag.OuterXml;
Complete code:
// 1 Canonicalize the SignedInfo tag
XmlDsigExcC14NTransform serializer = new XmlDsigExcC14NTransform();
XmlDocument doc = new XmlDocument();
string toBeCanonicalized = signedInfoTag.InnerXml;
doc.LoadXml(toBeCanonicalized);
serializer.LoadInput(doc);
string c14n = new StreamReader((Stream)serializer.GetOutput(typeof(Stream))).ReadToEnd();
// 2 Hash the SignedInfo tag
SHA256 HashAlg = SHA256.Create();
byte[] hash = HashAlg.ComputeHash(Encoding.UTF8.GetBytes(c14n));
// 3 Sign the hash
byte[] signature;
using (RSACryptoServiceProvider csp = new RSACryptoServiceProvider())
{
csp.ImportParameters(((RSACryptoServiceProvider)mCertificate.PrivateKey)
.ExportParameters(true));
signature = csp.SignData(Encoding.UTF8.GetBytes(c14n), "SHA256");
}
signValueTag.InnerText = Convert.ToBase64String(signature);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With