验证数字签名 XML 始终为 false

verify Digitaly sign an XML always false

我使用以下脚本创建键值

    sn.exe -k KeyFile.snk
sn.exe -m y
sn.exe -i KeyFile.snk test

然后我用它来验证和签署我的 xml 使用下面的代码片段

    private void SignXml(XmlDocument xmlDoc )
    {

        CspParameters parms = new CspParameters(1);         // PROV_RSA_FULL
        parms.Flags = CspProviderFlags.UseMachineKeyStore;  // Use Machine store
        parms.KeyContainerName = "test";                // "CodeProject" container
        parms.KeyNumber = 2;                                // AT_SIGNATURE
        RSACryptoServiceProvider csp = new RSACryptoServiceProvider(parms);

        // Creating the XML signing object.
        SignedXml sxml = new SignedXml(xmlDoc);
        sxml.SigningKey = csp;

        // Set the canonicalization method for the document.
        sxml.SignedInfo.CanonicalizationMethod =
            SignedXml.XmlDsigCanonicalizationUrl; // No comments.

        // Create an empty reference (not enveloped) for the XPath
        // transformation.
        Reference r = new Reference("");

        // Create the XPath transform and add it to the reference list.
        r.AddTransform(new XmlDsigEnvelopedSignatureTransform(false));

        // Add the reference to the SignedXml object.
        sxml.AddReference(r);

        // Compute the signature.
        sxml.ComputeSignature();

        // Get the signature XML and add it to the document element.
        XmlElement sig = sxml.GetXml();
        if (xmlDoc.DocumentElement != null) 
            xmlDoc.DocumentElement.AppendChild(sig);

    }
  public static Boolean VerifyXml(XmlDocument doc)
        {

            // Get the XML content from the embedded XML public key.
            Stream s = null;
            string xmlkey = string.Empty;
            try
            {
                s = typeof(Program).Assembly.GetManifestResourceStream(
                    "LicenceVerifier.PubKey.xml");

                // Read-in the XML content.
                StreamReader reader = new StreamReader(s);
                xmlkey = reader.ReadToEnd();
                reader.Close();
            }
            catch (Exception e)
            {
                Console.Error.WriteLine("Error: could not import public key: {0}",
                    e.Message);
                return false;
            }

            // Create an RSA crypto service provider from the embedded
            // XML document resource (the public key).
            RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
            csp.FromXmlString(xmlkey);

            // Create the signed XML object.
            SignedXml sxml = new SignedXml(doc);

            try
            {
                // Get the XML Signature node and load it into the signed XML object.
                XmlNode dsig = doc.GetElementsByTagName("Signature",
                    SignedXml.XmlDsigNamespaceUrl)[0];
                sxml.LoadXml((XmlElement)dsig);
            }
            catch
            {
                Console.Error.WriteLine("Error: no signature found.");
                return false;
            }

            // Verify the signature.
            if (sxml.CheckSignature(csp))
                return true;
            else
                return false;

验证总是 return 错误的问题,即使我使用密钥的 public 密钥,知道如何解决这个问题,签名部分看起来像:

  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <Reference URI="">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue>XT/TOXNZ6SEe6V3c6Ulxa/rOzLE=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>t1C/ycVh/8nV1uvc9WKbOTawKQjg3luUi7717AQDHc4N+g7DDHYHAb2zvoSEUTCHIkY9UFenoZqjbLwL9/ejyef/kQe8V/jrj0GZ60BNp8ee0nXSfr91wEdhOo9qqSo/iPbnP8By9tJnbOcJG7EFWjorgMITfHGct4QXfMZFoh4=</SignatureValue>
  </Signature>

我使用它们就像

SignXml(xmlDoc); // where xmlDoc is the xmldocument i create to be signed 
///////////////// TO Verify //////////////
            try
            {
                // Create a new CspParameters object to specify
                // a key container.

                Console.WriteLine("Type path");
                var path = Console.ReadLine();

                // Create a new XML document.
                XmlDocument xmlDoc = new XmlDocument();

                // Load an XML file into the XmlDocument object.
                xmlDoc.PreserveWhitespace = true;
                xmlDoc.Load(path);

                // Verify the signature of the signed XML.
                Console.WriteLine("Verifying signature...");
                bool result = VerifyXml(xmlDoc);

                // Display the results of the signature verification to 
                // the console.
                if (result)
                {
                    Console.WriteLine("The XML signature is valid.");
                }
                else
                {
                    Console.WriteLine("The XML signature is not valid.");
                }

                Console.ReadLine();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

在我看来,您检索公钥的方式有误。首先,检查文件 LicenceVerifier.PubKey.xml 是否将 Build Action 设置为 Embedded Resource.

然后运行调试中的这段代码:

typeof(Program).Assembly.GetManifestResourceNames()

并确认已将正确的资源名称传递给 GetManifestResourceStream。我认为您的代码应如下所示:

var asm = typeof(Program).Assembly;
s = asm.GetManifestResourceStream(asm.GetName().Name + ".LicenceVerifier.PubKey.xml");

或者如果 public 密钥位于子文件夹中:

var subFolder = "NAME";
var asm = typeof(Program).Assembly;
s = asm.GetManifestResourceStream(asm.GetName().Name + "." + subFolder + ".LicenceVerifier.PubKey.xml");

如果仍然无效,则表示您的 public 密钥与私人密钥不匹配。要确认尝试以这种方式修改您的代码:

private static string SignXml(XmlDocument xmlDoc)
{
   ...
   return csp.ToXmlString(false);
}

public static Boolean VerifyXml(XmlDocument doc, string xmlKey)
{
    RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
    csp.FromXmlString(xmlKey);
    ...
}

var xmlKey = SignXml(xml);
var res = VerifyXml(xml, xmlKey);

如果我的怀疑得到证实,那么只需将 LicenceVerifier.PubKey.xml 的内容替换为 SignXml 的修改版本返回的 xml .

在验证器上确保你有

xmlDoc.PreserveWhitespace = false;