我提前为相当冗长的代码块道歉,但它是我可以生成的最小的可编译示例。我已经省略了原始代码中的所有错误检查。我使用的是Visual Studio 2012和.NET 4.5,虽然这对4.5来说并不新鲜,但它可以适用于任何版本。
我正在尝试签署XML文档的元素以防止它们被篡改。我不想保护整个文件,只保留某些元素。也许甚至不同的元素与不同的键。
但是,当我签署三个示例元素并尝试验证它们时,第一个元素总是验证,另外两个元素失败。更糟糕的是,如果我在签名后修改它,第一个甚至会成功。我已经google了很多,阅读了很多教程,甚至asked a theoretical question here,但我不知道我做错了什么。任何人都可以发现我的错误吗?
注意:我非常乐意为解决此问题的人提供星期五问题的同样赏金。
证书是通过执行:
创建的“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年 - 签署-ss my
Test 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;
}
}
}
答案 0 :(得分:3)
您的测试代码中存在错误。第二个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);
}
需要注意的一个重要细节:PreserveWhitespace
也需要设置为true
{/ 1}}。