我试图按照微软的指令验证一个签名的XML文档,但是CheckSignature总是返回false。我使用的是.NET 4.5。最初,我没有使用C14转换,但这不起作用,在.NET 4.5中建议使用它作为解决方案,但不起作用。请注意,对于签名和验证,保留空白是正确的。
在代码片段中,为了简单起见,我删除了错误检查代码。是的,我检查了类似的问题,但没有找到答案。
证书(.PFX和.CER)
签名和验证是通过文件(而不是存储区)上的证书完成的:
到目前为止,证书还不错。个人存储显示我的签名证书与证书和密钥图标。
签名XML文档
现在,对于签名XML,我使用以下代码:
static void SignXML(XmlDocument xmlDoc, RSA Key)
{
SignedXml signedXml = new SignedXml(xmlDoc);
if (Properties.Settings.Default.UseC14Transform)
{
// http://stackoverflow.com/questions/13632630/signedxml-checksignature-fails-in-net-4-but-it-works-in-net-3-5-3-or-2
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
}
// Add the key to the SignedXml document.
signedXml.SigningKey = Key;
// Create a reference to be signed.
Reference reference = new Reference();
reference.Uri = "";
if (Properties.Settings.Default.UseC14Transform)
{
reference.AddTransform(new XmlDsigExcC14NTransform()); // required to match doc
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);
}
else
{
// Add an enveloped transformation to the reference.
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);
}
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
// Compute the signature.
signedXml.ComputeSignature();
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedXml.GetXml();
// Append the element to the XML document.
xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));
}签名代码如下所示:
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
using (StringWriter _writer = new StringWriter())
{
XmlSerializer serializer = new XmlSerializer(typeof(Models.Protected), new Type[] { param1.GetType() });
serializer.Serialize(_writer, param1);
doc.LoadXml(_writer.ToString());
}
// get RSA key from certificate (.PFX file)
X509Certificate2 cert = new X509Certificate2(certPrivateKeyFilePath, certFilePwd);
RSACryptoServiceProvider rsaKey = (RSACryptoServiceProvider)cert.PrivateKey;
SignXML(doc, rsaKey);
return Convert.ToBase64String(Encoding.UTF8.GetBytes(doc.OuterXml));签名的验证
我的验证方法如下所示:
static Boolean VerifyXml(XmlDocument Doc, X509Certificate2 cert)
{
RSACryptoServiceProvider rsaKey = (RSACryptoServiceProvider)cert.PublicKey.Key;
SignedXml signedXml = new SignedXml(Doc);
XmlNodeList nodeList = Doc.GetElementsByTagName("Signature");
signedXml.LoadXml((XmlElement)nodeList[0]);
return signedXml.CheckSignature(rsaKey);
}这反过来又会被这样调用:
Models.Protected mdl = null;
try
{ // The certificate is in a .CER file, only has PUBLIC key
X509Certificate2 cert = new X509Certificate2(certPubKeyFilePath);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true;
xmlDoc.LoadXml(DocEncoding.GetString(Convert.FromBase64String(b64String)));
if (VerifyXml(xmlDoc, cert)
{
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signature");
xmlDoc.DocumentElement.RemoveChild(nodeList[0]);
XmlSerializer _serializer = new XmlSerializer(typeof(Models.Protected), new Type[] { typeof(Models.Protected) });
using (StringReader _reader = new StringReader(_safeXML))
{
_safe = (Models.Protected)_serializer.Deserialize(_reader);
}
}
}
catch { /* something here */ }
return _safe;
}其他
要签名的对象是一个可序列化的Models.Protected类。我已经证实了它是正确序列化的,所以问题不在于此。
签名过程生成一个带有预期XML签名的有效XL文件,然后将其转换为Base64字符串。
验证过程读取一个Base64字符串,我已经验证了该字符串与生成的字符串相同。这个解码时读取的B64字符串产生的签名文档与签名时生成的文档完全相同。
生成的两个已签名的MD5的MD5哈希与验证之前获得的签名XML的MD5哈希相同。因此,他们是绝对平等的,他们的签名也是如此。
因此,我无法理解为什么CheckSignature返回false,尽管内容是相同的,并且正确地生成了证书(.PFX和.CER)。任何地方都不会抛出任何异常,它只是返回false。
有人能发现我做错了什么吗?
发布于 2016-09-08 19:05:43
由于@bartonjs的巨大提示,我可以验证在符号中发现的模数,并验证证书是不一样的。正如我所提到的,我在问题发生时和在新的(固定)情况下都使用了PluralSight免费SelfCert GUI。
没有对代码进行任何更改,问题就在证书上,即使用于验证的.CER文件是从存储的证书中生成的。
差别是可以运作的。如果让SelfCert.exe在存储上安装证书,验证方法就会失败。另一方面,在生成.PFX文件之后,我使用了而不是,使用了GUI上的"Save“按钮,而是使用生成的.PFX文件在存储区手动安装了带有私钥的证书,然后才使用存储的证书将其导出为.CER文件。现在模数相同,签名/验证过程顺利进行,没有失败。
https://stackoverflow.com/questions/39358493
复制相似问题