XML验证失败,SignedXml.CheckSignature始终返回false

时间:2016-09-06 22:01:32

标签: .net xml .net-4.5 x509 xml-signature

我正在尝试按照Microsoft指令验证签名的XML文档,但CheckSignature始终返回false。我使用的是.NET 4.5。最初我没有使用C14转换但是没有用,它被建议作为.NET 4.5中的解决方案,但不起作用。请注意,保留空白对于签名和验证都是正确的。

在代码段中,为了简单起见,我删除了错误检查代码。 是的,我检查了类似的问题,但没有找到答案。

证书(.PFX和.CER)

签名和验证是通过文件(而不是商店)上的证书完成的:

  • 使用PluralSight SelfCert自签名证书工具
  • 使用受密码保护的可导出私钥为数字签名创建个人自签名证书。我可以使用 certmgr.msc
  • 在我的个人商店中查看证书
  • 我通过包含公钥和私钥的签名证书(.PFX)保存
  • 我使用 certmgr.msc 导出证书而不是私钥,所以只使用公钥。这仅适用于 verifcation ,并保存为.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。

有人能发现我做错了吗

1 个答案:

答案 0 :(得分:0)

由于@bartonjs提供了很好的提示,我可以验证标志中发现的模数并验证证书是否相同。正如我所提到的,我在问题发生时和新的(固定)情况下都使用了PluralSight免费的SelfCert GUI。

未对代码进行任何更改,即使用于验证的.CER文件是从存储的证书中生成的,但问题仍然存在于证书上。

差异在于运作。如果我让SelfCert.exe在商店上安装证书,验证方法将失败。另一方面,如果生成.PFX文件后,我使用GUI上的“保存”按钮,而是使用生成的.PFX文件手动将证书安装在商店中键然后才使用存储的证书将其导出为.CER文件。现在模数是相同的,并且符号/验证过程顺利进行而不会失败。