XmlElement的Xml签名验证失败



我提前为这段相当长的代码块道歉,但这是我能生成的最小的可编译示例。我已经省略了原始代码中的所有错误检查。我使用的是Visual Studio 2012和。net 4.5,虽然这对4.5来说并不是什么新鲜事,但它应该可以在任何版本上工作。

我正在尝试签名一个XML文档的元素,以保护他们不被篡改。我不想保护整个文档,而只想保护某些元素。甚至可能不同的元素有不同的键。

然而,当我签名三个示例元素并尝试验证它们时,第一个总是验证,其他两个失败。更糟糕的是,如果我在签名后修改它,第一个甚至会成功。我在谷歌上搜索了很多,读了很多教程,甚至问了一个理论问题,但我不知道我做错了什么。有人能指出我的错误吗?

注:我很乐意提供同样的赏金,谁在周五的问题解决这个问题。

证书是通过执行:

创建的。

"C:Program Files (x86)Microsoft sdk Windowsv7.1ABinmakecert" -r -pe -n "CN=XMLDSIG_Test" -b 01/01/2013 -e 01/01/2014 -sky signing -ss my

测试xml文件是:

<?xml version="1.0" encoding="utf-8" ?>
<PackageRoot>
  <Package>
    <Changes >
     <Change/>
    </Changes>
  </Package>
  <Package>
    <Changes>
     <Change/>
     <Change/>
    </Changes>
  </Package>
  <Package>
    <Changes>
     <Change/>
     <Change/>
     <Change/>
    </Changes>
  </Package>
</PackageRoot>

签名和验证的代码:

namespace SOExample
{
  using System;
  using System.Security.Cryptography.X509Certificates;
  using System.Security.Cryptography.Xml;
  using System.Xml;
  public static class Program
  {
    public static void Sign(this XmlElement element, X509Certificate2 certificate)
    {
        var identifier = Guid.NewGuid().ToString();
        element.SetAttribute("Id", identifier);
        var signedXml = new SignedXml(element) { SigningKey = certificate.PrivateKey };
        var reference = new Reference("#" + identifier);
        reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
        signedXml.AddReference(reference);
        signedXml.ComputeSignature();
        var xmlDigitalSignature = signedXml.GetXml();
        element.AppendChild(element.OwnerDocument.ImportNode(xmlDigitalSignature, true));
    }
    public static bool VerifySignature(this XmlElement element, X509Certificate2 certificate)
    {
      var signedXml = new SignedXml(element);
      XmlNodeList nodeList = element.GetElementsByTagName("Signature");
      if (nodeList.Count != 1) return false;
      signedXml.LoadXml((XmlElement)nodeList[0]);
      return signedXml.CheckSignature(certificate, true);
    }
    public static void Main()
    {
        var xmlDoc = new XmlDocument { PreserveWhitespace = true };
        xmlDoc.Load("ExamplePackage.xml");
        var certificate = GetCertificateBySubject("CN=XMLDSIG_Test");
        foreach (XmlElement root in xmlDoc.GetElementsByTagName("PackageRoot"))
        {
          foreach (XmlElement package in root.GetElementsByTagName("Package"))
          {
            package.Sign(certificate);
          }
        }
        xmlDoc.Save("test_signed.xml");
        Console.WriteLine("XML file signed.");
        Console.WriteLine("Press any key to verify");
        Console.ReadLine();
        var signedDoc = new XmlDocument();
        signedDoc.Load("test_signed.xml");
        foreach (XmlElement root in xmlDoc.GetElementsByTagName("PackageRoot"))
        {
          foreach (XmlElement package in root.GetElementsByTagName("Package"))
          {
            Console.Write("Verifying Package " + package.GetAttribute("Id"));
            var success = package.VerifySignature(certificate);
            Console.WriteLine(success ? " successful!" : " failed!");
          }
        }
        Console.WriteLine("Done.");
        Console.ReadLine();
    }
    private static X509Certificate2 GetCertificateBySubject(string certificateSubject)
    {
      var store = new X509Store("My", StoreLocation.CurrentUser);
      store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
      foreach (X509Certificate2 c in store.Certificates)
      {
        if (c.Subject == certificateSubject)
        {
          store.Close();
          return c;
        }
      }
      store.Close();
      return null;
    }
  }
}

您的测试代码中有一个错误。第二次foreach再次在xmlDoc而不是signedDoc上循环。修复此问题将使所有节点的结果变为失败。
我还不知道他们为什么会失败。


我不知道为什么你的代码会失败,但我找到了一种方法让它工作。区别:所有签名都是根元素的直接子元素:

public static void Main()
{
    // ...
    var signedDoc = new XmlDocument { PreserveWhitespace = true };
    signedDoc.Load("test_signed.xml");
    foreach (XmlElement root in signedDoc.GetElementsByTagName("PackageRoot"))
    {
        foreach (XmlElement signature in root.GetElementsByTagName("Signature"))
        {
            var success = signature.VerifySignature(certificate);
            Console.WriteLine(success ? " successful!" : " failed!");
        }
    }
    Console.WriteLine("Done.");
    Console.ReadLine();
}
public static void Sign(this XmlElement element, X509Certificate2 certificate)
{
    var identifier = Guid.NewGuid().ToString("N");
    element.SetAttribute("Id", identifier);
    var signedXml = new SignedXml(element) { SigningKey = certificate.PrivateKey };
    signedXml.AddReference(new Reference("#" + identifier));
    signedXml.ComputeSignature();
    var xmlDigitalSignature = signedXml.GetXml();
    element.OwnerDocument.DocumentElement.AppendChild(
        element.OwnerDocument.ImportNode(xmlDigitalSignature, true));
}
public static bool VerifySignature(this XmlElement element, X509Certificate2 certificate)
{
    var signedXml = new SignedXml(element.OwnerDocument);
    signedXml.LoadXml(element);
    return signedXml.CheckSignature(certificate, true);
}

需要注意的一个重要细节:signedDoc也需要将PreserveWhitespace设置为true

最新更新