如何在C#中验证经过数字签名的XML文档?

时间:2016-02-25 01:33:03

标签: c# xml digital-signature signing xml-dsig

我正在尝试为我的软件构建更新程序。到目前为止,这不是一个艰巨的任务,但我想签署文件,以防止受到黑客攻击和修改(因为它将允许安装有害的软件)。

我在MSDN和各种博客中找到了一些教程,它们完美地展示了如何签署XML文件。得到了这个 - 我有一个签名附加到我的文件。

以某种方式未涵盖的事情是:验证如何在不同的计算机上运行?我不知道如何提供必要的数据来验证它。据我所知,我需要私钥才能验证签名(包含公钥)。现在我该怎么提供那个呢?如果我只是将它存储在应用程序中,即使加密也可以轻松抓取它。

我尝试过的另一种可能的方法是嵌入X509证书。我甚至得到了一些代码来生成这样的代码,但随后它将始终显示证书来自未知来源。

有没有提示用户安装证书的方法?或者更好的是没有安装东西?

到目前为止,我还没有找到任何关于此事的内容。

2 个答案:

答案 0 :(得分:3)

忘记这是XML。

数字签名依赖于加密的简单原则,更具体地说是依赖密码术,其中有2个密钥(公共密钥和私有密钥)。

您使用私钥进行签名并将签名文档提供给某人。有人用您的公钥验证签名。公钥 - 正如其名称所示 - 是公开的,可以分发。私钥仅用于签名。公众仅用于验证签名。

对于XML,您可以使用digital signature个人资料。您可以签署一个XML文档,该文档将生成一些可以附加到XML的二进制内容。您还可以附加公钥。由于公钥将成为已签名内容的一部分,因此您知道它也未被篡改。此外,您可以将公钥视为PKI的一部分。这可能是您首先选择信任公钥的方式。

签名内容提供:

  • 完整性
  • 不可否认性

关于验证,在Wikipedia和许多其他网站上解释了高级原则。您必须告诉您的应用程序在何处找到用于验证XML的密钥。

有关更多示例,请查看standardization body

最后,MSDN上有很多关于该主题的文章和示例代码。快速谷歌提出了这篇文章:How to: Verify the Digital Signatures of XML Documents

道路的另一个链接......这是primer on crypto,写得非常好。它讨论了密钥及其用法。

答案 1 :(得分:1)

感谢David Brossard的回答,我找到了解决方案。对于那些可能会徘徊的人来说,这里是我的代码(签名代码必须稍加修改,因为它包含我的签名工具中的内容):

<强>登录

    private static int Sign(Options options)
    {
        XmlDocument document = new XmlDocument {PreserveWhitespace = false};
        try
        {
            document.Load(options.File);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Invalid XML file: {0}.", ex.Message);
            return -3;
        }

        XmlElement signature;
        try
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
            if (!string.IsNullOrEmpty(options.Key) && File.Exists(options.Key))
            {
                using (StreamReader reader = new StreamReader(options.Key))
                    rsa.FromXmlString(reader.ReadToEnd());
            }
            else
            {
                FileInfo fi = new FileInfo(options.File);
                if (fi.DirectoryName == null) return -7;
                string keyFile = Path.Combine(fi.DirectoryName, "signature.key");
                using (StreamWriter writer = new StreamWriter(keyFile))
                    writer.Write(rsa.ToXmlString(true));
            }


            SignedXml signedXml = new SignedXml(document) {SigningKey = rsa};
            KeyInfo info = new KeyInfo();
            info.AddClause(new RSAKeyValue(rsa));
            signedXml.KeyInfo = info;

            Reference reference = new Reference {Uri = ""};

            reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            reference.AddTransform(new XmlDsigC14NTransform());

            signedXml.AddReference(reference);
            signedXml.ComputeSignature();

            signature = signedXml.GetXml();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error signing XML file: {0}.", ex.Message);
            return -4;
        }

        try
        {
            if (document.DocumentElement == null)
            {
                Console.WriteLine("Document has no document element.");
                return -6;
            }
            document.DocumentElement.AppendChild(document.ImportNode(signature, true));
            document.Save(options.File);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error saving signed XML file: {0}.", ex.Message);
            return -5;
        }

        return 0;
    }

<强>验证

    public static bool Verify(XmlDocument document)
    {
        if (document == null) throw new ArgumentNullException(nameof(document), "XML document is null.");

        SignedXml signed = new SignedXml(document);
        XmlNodeList list = document.GetElementsByTagName("Signature");
        if (list == null)
            throw new CryptographicException($"The XML document has no signature.");
        if (list.Count > 1)
            throw new CryptographicException($"The XML document has more than one signature.");

        signed.LoadXml((XmlElement)list[0]);

        RSA rsa = null;
        foreach (KeyInfoClause clause in signed.KeyInfo)
        {
            RSAKeyValue value = clause as RSAKeyValue;
            if (value == null) continue;
            RSAKeyValue key = value;
            rsa = key.Key;
        }

        return rsa != null && signed.CheckSignature(rsa);
    }