为XmlElement的XML签名无法验证 [英] Xml Signature for XmlElement fails to verify
问题描述
我提前代码相当长的块道歉,但它是最小的编译例子,我可以生产。我已经省略了所有的错误,从原来的代码检查。我使用Visual Studio 2012中和.NET 4.5,虽然这不是什么新鲜事至4.5,它应该与任何版本。
我试图签署的XML文档元素,以保护他们免受篡改。我不希望保护整个文档,但只有某些元素。甚至不同的元素用不同的密钥。
然而,当我签署三个示例元素,并尝试验证它们,第一个总是验证,其他两个失败。为了使事情变得更糟,如果我签字后修改的第一个甚至成功。我用Google搜索了很多,看了很多教程,甚至问一个理论问题,这里,但我没有任何线索,我在做什么错。 ?任何人能发现我的错误
请注意:我会很乐意提供同样的奖金是上周五的问题有人解决这个
该证书由执行创建:
C:\Program文件(x86)\微软SDKs\Windows\v7.1A\Bin\makecert-r -pe -nCN = XMLDSIG_Test-b 2013年1月1日-e 2014年1月1日签署-sky我-sS
块引用>
测试XML文件是:
<?XML版本=1.0编码=UTF-8>?;
< PackageRoot>
<包装及GT;
<&变化GT;
<改变/>
< /变更>
< /包装及GT;
<包装及GT;
<&变化GT;
<改变/>
<改变/>
< /变更>
< /包装及GT;
<包装及GT;
<&变化GT;
<改变/>
<改变/>
<改变/>
< /变更>
< /包装及GT;
< / PackageRoot>
要签名和验证的代码:
使用系统命名空间SOExample
{
;使用System.Security.Cryptography.X509Certificates
;
使用System.Security.Cryptography.Xml;
使用的System.Xml;
公共静态类节目
{
公共静态无效的注册(这XmlElement的元素,X509Certificate2证书)
{
变种标识= Guid.NewGuid()的ToString();
element.SetAttribute(ID,标识符);
变种signedXml =新SignedXml(元素){SigningKey = certificate.PrivateKey};
VAR基准=新的参考(#+标识符);
reference.AddTransform(新XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(参考);
signedXml.ComputeSignature();
VAR xmlDigitalSignature = signedXml.GetXml();
element.AppendChild(element.OwnerDocument.ImportNode(xmlDigitalSignature,真实));
}
公共静态布尔VerifySignature(这XmlElement的元素,X509Certificate2证书)
{
变种signedXml =新SignedXml(元);
XmlNodeList中节点列表= element.GetElementsByTagName(签名);
如果(nodeList.Count = 1!)返回false;
signedXml.LoadXml((XmlElement的)节点列表[0]);
返回signedXml.CheckSignature(证书,真正的);
}
公共静态无效的主要()
{
变种xmlDoc中=新的XmlDocument {PreserveWhitespace =真};
xmlDoc.Load(ExamplePackage.xml);
无功证书= GetCertificateBySubject(CN = XMLDSIG_Test);
的foreach(在xmlDoc.GetElementsByTagName的XmlElement根(PackageRoot))
{
的foreach(XmlElement的包root.GetElementsByTagName(包))
{
package.Sign(证书);
}
}
xmlDoc.Save(test_signed.xml);
Console.WriteLine(XML文件签名。);
Console.WriteLine(按任意键来验证);
到Console.ReadLine();
变种signedDoc =新的XmlDocument();
signedDoc.Load(test_signed.xml);
的foreach(在xmlDoc.GetElementsByTagName的XmlElement根(PackageRoot))
{
的foreach(XmlElement的包root.GetElementsByTagName(包))
{
Console.Write(验证包+ package.GetAttribute(ID));
VAR成功= package.VerifySignature(证书);
Console.WriteLine(成功成功了!?!失败);
}
}
Console.WriteLine(完成。);
到Console.ReadLine();
}
私有静态X509Certificate2 GetCertificateBySubject(字符串certificateSubject)
{
变种店=新的X509Store(我,StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
的foreach(在store.Certificates X509Certificate2 C)
{
如果(c.Subject == certificateSubject)
{
store.Close();
返回℃;
}
}
store.Close();
返回NULL;
}
}
}
解决方案您必须在您的测试代码中的错误。第二个的foreach超过
xmlDoc中
而不是signedDoc的
再次循环。修复这将改变结果失败的所有节点上。结果
为什么他们失败了,我不知道。
我找不到他们为什么失败,你的代码,但我找到了一种方法,使其工作。
的区别:所有的签名是根元素的直接孩子的:公共静态无效的主要()
{
// ...
变种signedDoc =新的XmlDocument {PreserveWhitespace =真};
signedDoc.Load(test_signed.xml);
的foreach(在signedDoc.GetElementsByTagName的XmlElement根(PackageRoot))
{
的foreach(在root.GetElementsByTagName XmlElement的签名(签名))
{
VAR成功= signature.VerifySignature(证书);
Console.WriteLine(成功成功了!?!失败);
}
}
Console.WriteLine(完成。);
到Console.ReadLine();
}
公共静态无效的注册(这XmlElement的元素,X509Certificate2证书)
{
变种标识= Guid.NewGuid()的ToString(N)。
element.SetAttribute(ID,标识符);
变种signedXml =新SignedXml(元素){SigningKey = certificate.PrivateKey};
signedXml.AddReference(新参考(#+标识符));
signedXml.ComputeSignature();
VAR xmlDigitalSignature = signedXml.GetXml();
element.OwnerDocument.DocumentElement.AppendChild(
element.OwnerDocument.ImportNode(xmlDigitalSignature,真实));
}
公共静态布尔VerifySignature(这XmlElement的元素,X509Certificate2证书)
{
变种signedXml =新SignedXml(element.OwnerDocument);
signedXml.LoadXml(元);
返回signedXml.CheckSignature(证书,真正的);
}
一个重要的细节需要注意:
PreserveWhitespace
必须设置为真正
为signedDoc
了。I apologize in advance for the rather lengthy block of code, but it's the smallest compilable example I could produce. I already omitted all error checking from the original code. I'm using Visual Studio 2012 and .NET 4.5, although this is nothing new to 4.5, it should work with any version.
I am trying to sign an XML documents' elements to protect them from tampering. I don't want to protect the whole document, but only certain elements. Maybe even different elements with different keys.
However, when I sign three example elements and try to verify them, the first one always verifies, the other two fail. To make it even worse, the first one even succeeds if I modify it after being signed. I have googled a lot, read a lot of tutorials and even asked a theoretical question here, but I don't have any clue what I'm doing wrong. Can anybody spot my mistake?
Note: I'd be more than happy to offer the same bounty that's on friday's question to anybody solving this.
The certificate was created by executing:
"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 signing -ss my
The Test xml file is:
<?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>
The code to sign and verify:
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; } } }
解决方案You have a bug in your test code. The second foreach loops again over
xmlDoc
instead ofsignedDoc
. Fixing this will change the outcome to fail for all nodes.
Why they fail I don't yet know.
I couldn't find out why they fail with your code but I found a way to make it work. The difference: All signatures are direct childs of the root element:
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); }
One important detail to notice:
PreserveWhitespace
needs to be set totrue
forsignedDoc
, too.这篇关于为XmlElement的XML签名无法验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!