如何在C#中手动计算XML签名

时间:2016-09-08 16:03:03

标签: c# xml soap

我知道有一个用于签署XML文档的SignedXml类。但是我试图自己计算签名值,以了解究竟发生了什么。更确切地说,我正在尝试签署一个SOAP:SOAP消息的Body元素。 我手动创建了Signature标签,使其与模板匹配。此外,我已成功计算了摘要并将此值插入DigestValue标记。但是,我无法为SigantureValue标记计算正确的值。

我的方法是:

  1. 使用excl n14n transform
  2. 规范化SignedInfo标记
  3. 使用SHA256
  4. 散列规范化数据
  5. 使用RSACryptoServiceProvider签署哈希值
  6. 我的代码看起来像这样:

      // 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);
    

    我做错了什么?

    有效SOAP消息的示例如下:

    
    
    <?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>
    &#13;
    &#13;
    &#13;

2 个答案:

答案 0 :(得分:1)

尝试使用

string toBeCanonicalized = signedInfoTag.InnerXml;

而不是

string toBeCanonicalized = signedInfoTag.OuterXml;

完整代码:

// 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);

答案 1 :(得分:1)

在我试着用证书密钥加密,散列,签名之后,我试图检查是否有SignedXml类可用的源代码。感谢上帝有一个reference source for .NET。所以我去了SignedXml.ComputeSignature方法,看看它是如何工作的。 TL; DR ,这是代码,基于这些来源:

// 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)