如何在一个程序中RSA加密Xml文件并在另一个程序中解密?

时间:2014-04-02 16:13:34

标签: c# xml encryption cryptography rsa

我需要加密/解密整个XML文件。我在this MSDN post中使用RSA,但问题是我必须在一个程序中加密,一个Windows窗体程序并在Windows服务中解密。 Windows服务如何知道我为解密而生成的RSA密钥?

加密/解密代码是:

 public class EncryptDecrpt
    {
        public static void Encrypt(XmlDocument doc, string ElementToEncrypt, string EncryptionElementID, RSA Alg,
            string KeyName)
        {
            try
            {
                //Check the arguments
                if (doc == null)
                    throw new ArgumentNullException("doc");
                if (ElementToEncrypt == null)
                    throw new ArgumentNullException("ElementToEncrypt");
                if (EncryptionElementID == null)
                    throw new ArgumentNullException("EncryptionElementID");
                if (Alg == null)
                    throw new ArgumentNullException("Alg");
                if (KeyName == null)
                    throw new ArgumentNullException("KeyName");


                // Find the specified element in the XmlDocument object
                // and create a new XmlElement object
                XmlElement elementToEncrypt = doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;
                if (elementToEncrypt == null)
                    throw new XmlException("The specified element was not found");

                RijndaelManaged sessionKey = null;

                // Create a 256 bit Rijandel key
                sessionKey = new RijndaelManaged();
                sessionKey.KeySize = 256;

                EncryptedXml eXml = new EncryptedXml();

                byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);

                // Construct an EncryptedData object and populate
                // it with the desired encryption information

                EncryptedData edElement = new EncryptedData();
                edElement.Type = EncryptedXml.XmlEncElementUrl;
                edElement.Id = EncryptionElementID;

                edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
                // Encrypt the session key and add it to an EncryptedKey element
                EncryptedKey ek = new EncryptedKey();

                byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false);

                ek.CipherData = new CipherData(encryptedKey);

                ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);

                DataReference dRef = new DataReference();

                // Specify the EncryptedData URI
                dRef.Uri = "#" + EncryptionElementID;

                ek.AddReference(dRef);

                edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));

                KeyInfoName kin = new KeyInfoName();

                kin.Value = KeyName;

                ek.KeyInfo.AddClause(kin);

                edElement.CipherData.CipherValue = encryptedElement;

                // Replace the element from the original XmlDocument
                // object with the EncrytedData element
                EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
            }
            catch (Exception e)
            {
                // rethrow the exception
                throw e;
            }

        }

        public static void Decrypt(XmlDocument Doc, RSA Alg, string KeyName)
        {
            // Check the arguments.   
            if (Doc == null)
                throw new ArgumentNullException("Doc");
            if (Alg == null)
                throw new ArgumentNullException("Alg");
            if (KeyName == null)
                throw new ArgumentNullException("KeyName");

            // Create a new EncryptedXml object.
            EncryptedXml exml = new EncryptedXml(Doc);

            // Add a key-name mapping. 
            // This method can only decrypt documents 
            // that present the specified key name.
            exml.AddKeyNameMapping(KeyName, Alg);

            // Decrypt the element.
            exml.DecryptDocument();

        }

    }

Windows窗体程序中的代码是:

 private void SaveForm()
        {
            try
            {
                string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
                XDocument doc = new XDocument();
                XElement xml = new XElement("Info",
                    new XElement("DatabaseServerName", txtServerName.Text),
                    new XElement("DatabaseUserName", txtDatabaseUserName.Text),
                    new XElement("DatabasePassword", txtDatabasePassword.Text),
                    new XElement("ServiceAccount", txtAccount.Text),
                    new XElement("ServicePassword", txtServicePassword.Text),
                    new XElement("RegistrationCode", txtRegistrationCode.Text));

                doc.Add(xml);
                doc.Save(fileName);

                // Encrypt
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(fileName);
                // Create a new CspParameters object to specify
                // a key container
                CspParameters cspParams = new CspParameters();
                cspParams.KeyContainerName = "XML_ENC_RSA_KEY";

                RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);

                EncryptDecrpt.Encrypt(xmlDoc, "info", "EncryptedElement1", rsaKey, "rsaKey");

                xmlDoc.Save(fileName);

                MessageBox.Show(xmlDoc.OuterXml);

                EncryptDecrpt.Decrypt(xmlDoc, rsaKey, "rsaKey");

Windows服务如何知道RSA密钥或如何执行此操作?

2 个答案:

答案 0 :(得分:2)

您需要在两个应用程序之间共享公钥。每次只做new RSACryptoServiceProvider()会生成新密钥。我在项目的app.config中分享了我的密钥:

<applicationSettings>
    <YpurApp.Properties.Settings>
        <setting name="PublicKeyXml" serializeAs="String">
            <value>&lt;RSAKeyValue&gt;&lt;Modulus&gt;YOURMODULUS&lt;/Modulus&gt;&lt;Exponent&gt;YOUREXP&lt;/Exponent&gt;&lt;/RSAKeyValue&gt;</value>
        </setting>

一旦读入程序,XML实际上就像这样:

<RSAKeyValue><Modulus>YOURMODULUS</Modulus><Exponent>YOUREXP</Exponent></RSAKeyValue>

然后你可以使用这样的代码在公共应用程序中加密:

    private static byte[] Encrypt(byte[] bytes)
    {
        using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
        {
            rsa.FromXmlString(Properties.Settings.Default.PublicKeyXml);
            return rsa.Encrypt(bytes, true);
        }
    }

私有应用程序还应具有匹配的PrivateKey,无论是硬编码还是app.config。您不希望与任何可能使用它来破坏加密的“公共”共享私钥。使用私钥解密数据,如下所示:

    private static byte[] Decrypt(byte[] bytes)
    {
        using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
        {
            rsa.FromXmlString(Properties.Settings.Default.PrivateKeyXml);
            return rsa.Decrypt(bytes, true);
        }
    }

要生成密钥,请执行以下操作:

        CspParameters cspParams = new CspParameters();
        cspParams.KeyContainerName = "XML_ENC_RSA_KEY";
        RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
        string keyXML = rsaKey.ToXmlString(true);

这将包含公钥和私钥。公钥是<RSAKeyValue><Modulus></Modulus><Exponent></Exponent>部分。私钥就是整个事物,包括<P></P><Q></Q><DP></DP><DQ></DQ><InverseQ></InverseQ><D></D>。将该字符串复制/粘贴到您的app.config中,然后将其拆分为两个选项PublicKeyPrivateKey,如上所述。请勿在加密应用程序的app.config中包含私钥。

答案 1 :(得分:2)

PKI和密钥管理本身就是书籍。但它归结为:您在执行解密的服务器上创建密钥对。解密需要私钥,并且您不想移动私钥(除了备份目的)。你应该只做一次,比如5年。

然后将公钥 securily 分发给客户端。您可以通过在代码中加入密钥来完成此操作 - 只要您可以信任公钥即可。然后使用公钥来执行加密。同样,对于每个密钥对,通常执行一次分发。