1

I have a requirement to encrypt/decrypt a whole XML file. I am using RSA like in this MSDN post but the issue is that I have to encrypt in one program, an Windows Form program and decrypt in a Windows Service. How will the Windows Service know the RSA key I generated in order to decrypt?

The encrypt/decrypt code is:

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

        }

    }

The code in the Windows Form program is:

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

How will the Windows Service know the RSA key or how can I do this?

user2471435
  • 1,644
  • 7
  • 35
  • 62

2 Answers2

2

You need to share your public key between both of your applications. Just doing new RSACryptoServiceProvider() every time will generate new keys. I shared my keys in the project's 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>

The XML actually looks like this, once read in by the program:

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

Then you can use code like this to encrypt in the public application:

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

The private application should also have the matching PrivateKey, either hard coded or in the app.config. You would not want to share the private key with anything "public" that could use it to break your encryption. Data is decrypted using the private key like this:

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

To generate the keys, do this:

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

This will contain the public and private keys. The public key is the <RSAKeyValue><Modulus></Modulus><Exponent></Exponent> part. The private key is the whole thing, including the <P></P><Q></Q><DP></DP><DQ></DQ><InverseQ></InverseQ><D></D>. Copy/Paste that string into your app.config, and split it into two options, PublicKey and PrivateKey as noted above. DO NOT INCLUDE THE PRIVATE KEY in the encrypting application's app.config.

djs
  • 1,660
  • 1
  • 17
  • 35
  • 1
    Eh, if you do the encryption with the private key then you're doing it wrong. – Maarten Bodewes Apr 02 '14 at 17:01
  • Actually, in my app we did it that way on purpose. We encrypt some data, but don't want anyone else to be able to create the same encryption. We share the public key with the client application so it can be decrypted. But you are correct, in more traditional encryption scenarios. – djs Apr 02 '14 at 17:05
  • You should *sign* your data to enable non-repudiation. Please do not use this scenario as answer to a generic question on encryption. Please take a look at [this](http://crypto.stackexchange.com/q/2123/1172) and [this](http://crypto.stackexchange.com/q/3179/1172). – Maarten Bodewes Apr 02 '14 at 17:12
  • Ok seems like way to go but which RSA key is public key nd which is the private key? – user2471435 Apr 02 '14 at 17:13
  • Oh, my goodness :) In general the RSA public key is the one contained in a certificate, or otherwise it is "the shorter one" :P – Maarten Bodewes Apr 02 '14 at 17:14
  • I mean in my example. I don't understand where the two are in the code. – user2471435 Apr 02 '14 at 17:17
  • It *should* be represented by `"KeyName"` parameter in the `Decrypt` function. We cannot see if it actually is a private key though. – Maarten Bodewes Apr 02 '14 at 17:21
  • When I do this RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams); am I creating a public or private key? Where is the other one? I'm sorry, I don't know this subject area at all – user2471435 Apr 02 '14 at 17:31
  • edited to reflect (hopefully) the proper encryption/decryption scenario. – djs Apr 02 '14 at 17:59
  • Thanks for the edit. How do I reconstitute the XML file from the byte[] in your Decrypt function or can I use mine? – user2471435 Apr 02 '14 at 18:03
  • That's a whole different questions. [See this.](http://stackoverflow.com/questions/1500259/how-to-convert-an-xmldocument-to-an-arraybyte) – djs Apr 02 '14 at 18:08
  • I'm sorry. That's to go other way Xml to byte[]. In decrypt, how do I go byte[] back to the XmlDocument? – user2471435 Apr 02 '14 at 18:12
  • Looks a lot better this way, so +1 for providing sample code. – Maarten Bodewes Apr 02 '14 at 18:13
  • Where did the decrypt function go? – user2471435 Apr 02 '14 at 18:24
  • Where is the private key used? – user2471435 Apr 03 '14 at 13:12
  • I don't see how placing the key string in the Program Settings enables the other program to see it. – user2471435 Apr 03 '14 at 14:56
  • The public key is added to the encrypting app.config. The private key is added to the decrypting app.config. These keys need to match. See the end of my answer about generating the keys. – djs Apr 03 '14 at 15:41
  • Please see http://stackoverflow.com/questions/22844232/error-rsa-encrypting-xmlfile-converted-into-byte . The encrypt fails and I can't figure out why. – user2471435 Apr 03 '14 at 17:26
  • djs, its not working on the encrypt. Can you go to that topic and help? – user2471435 Apr 03 '14 at 18:53
  • [See this.](http://stackoverflow.com/questions/1496793/rsa-encryption-getting-bad-length) – djs Apr 03 '14 at 19:40
  • Why does the original code I published work then? I just wouldn't be able to get that solution onto two machines, – user2471435 Apr 07 '14 at 13:40
  • The actual error is a Syntax error on line:rsa.FromXmlString(Properties.Settings.Default.PublicKeyXml); – user2471435 Apr 07 '14 at 13:59
  • djs, its generating a bad length error no matter what key size I use http://stackoverflow.com/questions/22918835/rsa-exception-bad-length-r-n-noi-matter-what-my-key-size-is – user2471435 Apr 07 '14 at 19:22
2

PKI and key management are books in themselves. But it comes down to this: you create a key pair on the server that performs the decryption. Decryption requires the private key and you don't want to move the private key (except for backup purposes). You should do this only once in, say, 5 years.

Then you distribute the public key securily to the clients. You may do this by incorporating the key in your code - as long as you can trust the public key. The public key is then used to perform the encryption. Again, the distribution is normally performed once for each key pair.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263