XmlElement的Xml签名无法validation

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

我正在尝试签署XML文档的元素以防止它们被篡改。 我不想保护整个文档,但只保留某些元素。 也许甚至不同的元素与不同的键。

但是,当我签署三个示例元素并尝试validation它们时,第一个元素总是validation,另外两个元素失败。 更糟糕的是,如果我在签名后修改它,第一个甚至会成功。 我已经google了很多,阅读了很多教程,甚至在这里问了一个理论问题 ,但我不知道我做错了什么。 谁能发现我的错误?

注意:我很乐意为解决这个问题的人提供星期五问题的同样赏金。

证书是通过执行:

“C:\ Program Files(x86)\ Microsoft SDKs \ Windows \ v7.1A \ Bin \ makecert”-r -pe -n“CN = XMLDSIG_Test”-b 01/01/2013 -e 01/01/2014 -sky签约 – 我的

Test xml文件是:

                     

签名和validation的代码:

 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