【问题标题】:SignedXml.CheckSignature fails in .NET 4 but it works in .NET 3.5, 3 or 2SignedXml.CheckSignature 在 .NET 4 中失败,但在 .NET 3.5、3 或 2 中有效
【发布时间】:2012-11-17 22:20:21
【问题描述】:

我收到了来自第 3 方网络服务的响应。我使用该响应加载了一个 XmlDocument。

  string txt = readStream.ReadToEnd();
  response = new XmlDocument();
  response.PreserveWhitespace = true;
  response.LoadXml(txt);   
  return response;

现在我想验证是否使用证书对响应进行了签名。我有一个在msdn 上找到的VerifyXmlDoc(XmlDocument xmlDoc) 方法。

我知道消息是正确的。

    public bool VerifyXmlDoc(XmlDocument xmlDoc)
    {

        SignedXml signed = new SignedXml(xmlDoc);

        XmlNodeList signatureNodeList = xmlDoc.GetElementsByTagName("Signature");


        signed.LoadXml((XmlElement)signatureNodeList[0]);

        X509Certificate2 serviceCertificate = null;
        foreach (KeyInfoClause clause in signed.KeyInfo)
        {
            if (clause is KeyInfoX509Data)
            {
                if (((KeyInfoX509Data)clause).Certificates.Count > 0)
                {
                    serviceCertificate = (X509Certificate2)((KeyInfoX509Data)clause).Certificates[0];
                }
            }
        }


        bool result = signed.CheckSignature(serviceCertificate, true);
        return result;

    }

如果我将项目的目标框架设置为 .NET 3.5 或 .NET 3 或 .NET 2,效果会很好。结果是真的。但是如果我将目标框架更改为 .NET 4 结果是错误的。 (而且我必须使用 .NET 4)

关于如何解决这个问题的任何想法?

【问题讨论】:

标签: c# security certificate signature signedxml


【解决方案1】:

这是一个已知问题。 .NET 3.5 和 .NET 4.0 之间的规范化实现已经改变。

我不知道这是否适用于所有 XML 签名,但以下内容来自我所做的测试。

将以下 C14N Transform 类添加到您的项目中:

public class MyXmlDsigC14NTransform: XmlDsigC14NTransform {
  static XmlDocument _document;
  public static XmlDocument document {
    set {
      _document = value;
    }
  }

  public MyXmlDsigC14NTransform() {}

  public override Object GetOutput() {
    return base.GetOutput();
  }

  public override void LoadInnerXml(XmlNodeList nodeList) {
    base.LoadInnerXml(nodeList);
  }

  protected override XmlNodeList GetInnerXml() {
    XmlNodeList nodeList = base.GetInnerXml();
    return nodeList;
  }

  public XmlElement GetXml() {
    return base.GetXml();
  }

  public override void LoadInput(Object obj) {
    int n;
    bool fDefaultNS = true;

    XmlElement element = ((XmlDocument) obj).DocumentElement;

    if (element.Name.Contains("SignedInfo")) {
      XmlNodeList DigestValue = element.GetElementsByTagName("DigestValue", element.NamespaceURI);
      string strHash = DigestValue[0].InnerText;
      XmlNodeList nodeList = _document.GetElementsByTagName(element.Name);

      for (n = 0; n < nodeList.Count; n++) {
        XmlNodeList DigestValue2 = ((XmlElement) nodeList[n]).GetElementsByTagName("DigestValue", ((XmlElement) nodeList[n]).NamespaceURI);
        string strHash2 = DigestValue2[0].InnerText;
        if (strHash == strHash2) break;
      }

      XmlNode node = nodeList[n];

      while (node.ParentNode != null) {
        XmlAttributeCollection attrColl = node.ParentNode.Attributes;
        if (attrColl != null) {
          for (n = 0; n < attrColl.Count; n++) {
            XmlAttribute attr = attrColl[n];
            if (attr.Prefix == "xmlns") {
              element.SetAttribute(attr.Name, attr.Value);
            } else if (attr.Name == "xmlns") {
              if (fDefaultNS) {
                element.SetAttribute(attr.Name, attr.Value);
                fDefaultNS = false;
              }
            }
          }
        }

        node = node.ParentNode;
      }
    }

    base.LoadInput(obj);
  }
}

使用 CryptoConfig.AddAlgorithm 方法注册类。

CryptoConfig.AddAlgorithm(typeof(MyXmlDsigC14NTransform), "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"); 

var message = new XmlDocument();
message.PreserveWhitespace = true;
message.Load("XmlSig.xml");

MyXmlDsigC14NTransform.document = message; // The transform class needs the xml document

// Validate signature as normal.  

应该可以的。

【讨论】:

  • 为什么 _document 变量是静态的?
【解决方案2】:

尝试为 SignedXml 类的 SignedInfo 属性显式设置规范化方法。.Net 2.0 和 .Net 4.0 之间的默认行为似乎发生了变化

signed.SignedInfo.CanonicalizationMethod = Signed.XmlDsigExcC14NTransformUrl;

参考:

This answer

【讨论】:

  • 但 CanonicalizationMethod 是一个字符串
【解决方案3】:

我遇到了同样的问题,但这些答案都没有帮助我。在这种情况下,它是否有效取决于我使用的操作系统,而不是 .Net 版本。

我已通过在 app.config 中添加此代码来启用 SignedXML 日志,以查看背后发生了什么:

<system.diagnostics>
        <sources>
            <source name="System.Security.Cryptography.Xml.SignedXml" switchName="XmlDsigLogSwitch">
                <listeners>
                    <add name="logFile" />
                </listeners>
            </source>
        </sources>
        <switches>
            <add name="XmlDsigLogSwitch" value="Verbose" />
        </switches>
        <sharedListeners>
            <add name="logFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="XmlDsigLog.txt"/>
        </sharedListeners>
        <trace autoflush="true">
            <listeners>
                <add name="logFile" />
            </listeners>
        </trace>
    </system.diagnostics>

它写了这行:

System.Security.Cryptography.Xml.SignedXml Information: 17 : [SignedXml#033ec00f, UnsafeTransformMethod] Canonicalization method "http://www.w3.org/TR/1999/REC-xpath-19991116" is not on the safe list. Safe canonicalization methods are: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments", "http://www.w3.org/2001/10/xml-exc-c14n#", "http://www.w3.org/2001/10/xml-exc-c14n#WithComments", "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2000/09/xmldsig#base64", "urn:mpeg:mpeg21:2003:01-REL-R-NS:licenseTransform", "http://www.w3.org/2002/07/decrypt#XML".

我发现这篇 Microsoft 支持文章试图修复安全更新 3141780 引入的错误: https://support.microsoft.com/en-us/kb/3148821

在那篇文章中,在场景 2 部分,有 2 个解决方案,我修复了应用与 XPath 转换方法相关的注册表项的问题: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\Security\SafeTransformMethods@XmlDsigXPathTransform=http://www.w3.org/TR/1999/REC-xpath-19991116

【讨论】:

    【解决方案4】:
    // Assume the data to sign is in the data.xml file, load it, and
    
    // set up the signature object.
    XmlDocument doc = new XmlDocument();
    
    doc.Load(@"D:\Example.xml");
    SignedXml sig = new SignedXml(doc);
    
    // Make a random RSA key, and set it on the signature for signing.
    RSA key = new RSACryptoServiceProvider();
    
    sig.SigningKey = key;
    
    // Create a Reference to the containing document, add the enveloped
    // transform, and then add the Reference to the signature
    Reference refr = new Reference("");refr.AddTransform(new XmlDsigEnvelopedSignatureTransform());
    
    sig.AddReference(refr);
    
    // Compute the signature, add it to the XML document, and save
    sig.ComputeSignature();
    
    doc.DocumentElement.AppendChild(sig.GetXml());
    doc.Save("data-signed.xml");
    
    // Load the signed data
    
    //XmlDocument doc = new XmlDocument();
    doc.PreserveWhitespace = true;
    
    doc.Load("data-signed.xml");
    
    // Find the Signature element in the document
    XmlNamespaceManager nsm = new XmlNamespaceManager(new NameTable());
    
    nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl);
    XmlElement sigElt = (XmlElement)doc.SelectSingleNode("//dsig:Signature", nsm);
    
    // Load the signature for verification
    
    //SignedXml sig = new SignedXml(doc);
    
    sig.LoadXml(sigElt);
    
    // Verify the signature, assume the public key part of the
    
    // signing key is in the key variable
    if (sig.CheckSignature(key))
        Console.WriteLine("Signature verified");
    else
        Console.WriteLine("Signature not valid");
    

    【讨论】:

      【解决方案5】:

      从 .NET 框架 4/4.5 开始,使用 x509 证书和其他安全功能的类位于 System.IdentityModel.dll 中。在提到的命名空间中搜索相应的类。

      【讨论】:

        【解决方案6】:

        我解决了这个问题,将相同的命名空间从 Signature 标签添加到 SignedInfo。 像这样:

        之前:

        之后:

        【讨论】:

        • 试过了,没有任何区别(.NET 3.5 读取 .NET 4.0 生成的签名)
        【解决方案7】:

        为了在 NET 4.0+ 上检查签名,您必须更改 CanonicalizationMethod 的上下文,因此您必须按以下方式初始化您的 signedXml 对象:

        XmlNodeList signatureNodeList = xmlDoc.GetElementsByTagName("Signature");
        
        SignedXml signedXml = new SignedXml((XmlElement)signatureNodeList[0]);
        
        signedXml.LoadXml((XmlElement)signatureNodeList[0]);
        
        //Then you proceed your check as usual
        

        【讨论】:

          【解决方案8】:
          public static Boolean VerifyDetachedSignature(string XmlSigFileName)
          {   
              // Create a new XML document.
              XmlDocument xmlDocument = new XmlDocument();
          
              // Load the passed XML file into the document.
              xmlDocument.Load(XmlSigFileName);
          
              // Find the "Signature" node and create a new XmlNodeList object.
              XmlNodeList nodeList = xmlDocument.GetElementsByTagName("Signature");
          
              // Create a new SignedXMl object.
              SignedXml signedXml = new SignedXml();
          
              // Load the signature node.
              signedXml.LoadXml((XmlElement)nodeList[0]);
          
              // Check the signature and return the result. 
              return signedXml.CheckSignature();
          }
          

          【讨论】:

            猜你喜欢
            • 2010-10-11
            • 2011-04-04
            • 1970-01-01
            • 2012-09-05
            • 2014-07-10
            • 1970-01-01
            • 1970-01-01
            • 2019-09-05
            • 2012-03-14
            相关资源
            最近更新 更多