简体   繁体   中英

How do I validate digitally signed XML documents in C#?

I'm currently trying to build an updater for my software. So far this is not a hard task but I'd like to sign files in order to prevent harm in case these get hacked and modified (as it would allow installing harmful software).

I found some tutorials on MSDN and in various blogs which perfectly show how to sign an XML file. Got this working - I have a signature appended to my file.

The thing that somehow isn't covered is: How does validation work on different computers? I don't get how I should provide the necessary data to validate it. As far as I understood I need the private key in order to validate the signature (which contains the public key). Now how would I provide that one? If I simply store it in the application, it can be grabbed easily, even if encrypted.

Another possible approach I've tried was to embed a X509 certificate. I even got some code to generate such one, but then it'll always show that the certificate comes from an unknown source.

Is there any way without prompting the user for installing certificates? Or better without installing stuff at all?

So far I haven't found anything on that matter.

Forget the fact this is XML.

Digital signatures rely on the simple principle of cryptography and more specifically assymmetric cryptography where you have 2 keys (a public one and a private one).

You sign with your private key and give the signed document to someone. That someone validates the signature with your public key. The public key - as its name indicates - is public and can be distributed. The private key is only used for signing. The public is only used for validating the signature.

With respect to XML, you can use the digital signature profile. You can sign an XML document which will result in some binary content which you can attach to the XML. You can also attach the public key. Since the public key will be part of the signed content, you know it hasn't been tampered with either. Also, you could consider the public key to be part of a PKI. This could be how you choose to trust the public key in the first place.

Signing content provides:

  • integrity
  • non-repudiation

With respect to validation, the high-level principle is explained on Wikipedia and many other sites. You will have to tell your app where to locate the key with which to validate the XML.

Have a look at the standardization body for more examples.

Lastly, the MSDN has lots of articles and sample code on the topic. A quick google came up with this article: How to: Verify the Digital Signatures of XML Documents

One more link for the road... Here is a primer on crypto which is quite well written. It talks about keys and their usage.

Thanks to David Brossard's answer I've found the solution. Well for those who may prowl across that, here's my code (signing code must be modified a little as it contains stuff from my signing tool):

Signing

    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;
    }

Verification

    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);
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM