我正在尝试按照Microsoft指令验证签名的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));
验证XML签名
我的验证方法如下:
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字符串生成签名的XML文档,该文档与签名完成时生成的文档完全相同。
生成的签名XML的MD5 hashe与验证前获得的签名XML的MD5哈希相同。因此,他们的签名绝对平等。
因此,我无法理解为什么即使内容相同且证书正确生成(.PFX和.CER),CheckSignature也会返回false。在任何地方都不会抛出任何异常,它只会返回false。
有人能发现我做错了吗?
答案 0 :(得分:0)
由于@bartonjs提供了很好的提示,我可以验证标志中发现的模数并验证证书是否相同。正如我所提到的,我在问题发生时和新的(固定)情况下都使用了PluralSight免费的SelfCert GUI。
未对代码进行任何更改,即使用于验证的.CER文件是从存储的证书中生成的,但问题仍然存在于证书上。
差异在于运作。如果我让SelfCert.exe在商店上安装证书,验证方法将失败。另一方面,如果生成.PFX文件后,我不使用GUI上的“保存”按钮,而是使用生成的.PFX文件手动将证书安装在商店中键然后才使用存储的证书将其导出为.CER文件。现在模数是相同的,并且符号/验证过程顺利进行而不会失败。