Question

I was told not to use RSA to encrypt simple text but to use AES. I found a simple piece of code to implement AES:

public static class Crypto
    {
        #region Settings

        private static int _iterations = 2;
        private static int _keySize = 256;

        private static string _hash = "SHA1";
        private static string _salt = "aselrias38490a32"; // Random
        private static string _vector = "8947az34awl34kjq"; // Random

        #endregion

        public static string Encrypt(string value, string password)
        {
            return Encrypt<AesManaged>(value, password);
        }

        public static string Encrypt<T>(string value, string password)
            where T : SymmetricAlgorithm, new()
        {

            byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector);
            byte[] saltBytes = Encoding.ASCII.GetBytes(_salt);
            byte[] valueBytes = Encoding.UTF8.GetBytes(value);

            byte[] encrypted;
            using (T cipher = new T())
            {
                PasswordDeriveBytes _passwordBytes =
                    new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
                byte[] keyBytes = _passwordBytes.GetBytes(_keySize/8);

                cipher.Mode = CipherMode.CBC;

                using (ICryptoTransform encryptor = cipher.CreateEncryptor(keyBytes, vectorBytes))
                {
                    using (MemoryStream to = new MemoryStream())
                    {
                        using (CryptoStream writer = new CryptoStream(to, encryptor, CryptoStreamMode.Write))
                        {
                            writer.Write(valueBytes, 0, valueBytes.Length);
                            writer.FlushFinalBlock();
                            encrypted = to.ToArray();
                        }
                    }
                }
                cipher.Clear();
            }
            return Convert.ToBase64String(encrypted);
        }

        public static string Decrypt(string value, string password)
        {
            return Decrypt<AesManaged>(value, password);
        }

        public static string Decrypt<T>(string value, string password) where T : SymmetricAlgorithm, new()
        {
            byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector);
            byte[] saltBytes = Encoding.ASCII.GetBytes(_salt);
            byte[] valueBytes = Convert.FromBase64String(value);

            byte[] decrypted;
            int decryptedByteCount = 0;

            using (T cipher = new T())
            {
                PasswordDeriveBytes _passwordBytes = new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
                byte[] keyBytes = _passwordBytes.GetBytes(_keySize/8);

                cipher.Mode = CipherMode.CBC;

                try
                {
                    using (ICryptoTransform decryptor = cipher.CreateDecryptor(keyBytes, vectorBytes))
                    {
                        using (MemoryStream from = new MemoryStream(valueBytes))
                        {
                            using (CryptoStream reader = new CryptoStream(from, decryptor, CryptoStreamMode.Read))
                            {
                                decrypted = new byte[valueBytes.Length];
                                decryptedByteCount = reader.Read(decrypted, 0, decrypted.Length);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    return String.Empty;
                }

                cipher.Clear();
            }
            return Encoding.UTF8.GetString(decrypted, 0, decryptedByteCount);
        }

    }

However, this is based on a string coming back and then used to decrypt in the same program. I need to encrypt the following data in a WinForms program and the decrypt in a whole separate Windows Service program:

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

                // Convert XML doc to byte stream
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(fileName);

                // byte[] fileBytes = Encoding.Default.GetBytes(xmlDoc.OuterXml);

                string encrypted = Crypto.Encrypt(xmlDoc.OuterXml, "testpass");

How can I do it? Please show sample code.

EDIT: Kevin, I have implemented your algorithm but the problem is I want to generate the key once and save it for use in the other program to decrypt but I need to pass the byte[] to the encrypt function. So I tried converting using System.Text.Encoding.ASCII.GetBytes(key); and it doesn't do it correctly. I have the wrong number of bytes for byte[] for the key.

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

                // Read file to a string
                string contents = File.ReadAllText(fileName);

                string key = String.Empty;
                byte[] aesKey;
                using (var aes = Aes.Create())
                {
                    // aesKey = aes.Key;
                    key = Convert.ToBase64String(aes.Key);
                }

                string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
                aesKey = System.Text.Encoding.UTF8.GetBytes(sKey);

                string encyptedText = EncryptDecrpt.EncryptStringToBase64String(contents, aesKey);
                File.WriteAllText(fileName, encyptedText);

EDIT2: Here's both parts as they stand now. The encrypting side:

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

                // Read file to a string
                // string contents = File.ReadAllText(fileName);

                string key = String.Empty;
                byte[] aesKey;
                //using (var aes = Aes.Create())
                //{
                //    aesKey = aes.Key;
                //    key = Convert.ToBase64String(aes.Key);
                //}

                string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
                aesKey = Convert.FromBase64String(sKey);

                string encyptedText = EncryptDecrpt.EncryptStringToBase64String(doc.ToString(), aesKey);
                File.WriteAllText(fileName, encyptedText);
                //doc.Save(fileName);

The Windows Service side that tries to decrypt:

try
        {
            string path = AppDomain.CurrentDomain.BaseDirectory;
            eventLog1.WriteEntry(path);
            string fileName = System.IO.Path.Combine(path, "alphaService.xml");

            string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
            Byte[] keyBytes = Convert.FromBase64String(sKey);

            var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
            string xmlStr = DecryptStringFromBase64String(encryptedText, keyBytes);

            eventLog1.WriteEntry(xmlStr);

            using (XmlReader reader = XmlReader.Create(new StringReader(xmlStr)))
            {
                reader.ReadToFollowing("DatabaseServerName");
                DatabaseServerName = reader.ReadElementContentAsString();
                reader.ReadToFollowing("DatabaseUserName");
                DatabaseUserName = reader.ReadElementContentAsString();
                reader.ReadToFollowing("DatabasePassword");
                DatabasePassword = reader.ReadElementContentAsString();
                reader.ReadToFollowing("RegistrationCode");
                RegistrationCode = reader.ReadElementContentAsString();
            }
            eventLog1.WriteEntry("Configuration data loaded successfully");
        }
        catch (Exception ex)
        {
            eventLog1.WriteEntry("Unable to load configuration data.  " + ex.Message);
        }
Était-ce utile?

La solution

The algorithm I wrote below uses a random Initialization Vector that it puts at the beginning of the encrypted value so you can encrypt the same value twice and not get the same encrypted output. This is fairly normal and lets you only pass a single "secret" back and forth.

You will need to share your secret key by some out of bounds process because both encryption and decryption need to know the key. That is a seperate topic of key exchange that is documented in other places. Here is an SO link to get you started if you need some help on it.

Also if you are "making up" random values I recommend that you don't. Use something to help you like the following which generates random bytes and then converts them into a base64 string which is easier for human usage or some types of key exchange. Note that this is just an example of how you could generate random key's... in practice this may be based on some user input that is recreatable or you use the users hash value to lookup your random key that you generate. In any event here is the code for the key...

byte[] key;
string base64Key;
using (var aes = Aes.Create())
{
    // key as byte[]
    key = aes.Key;  
    // key as base64string - which one you use depends on how you store your keys
    base64Key= Convert.ToBase64String(aes.Key);
}

Usage is as follows...

    // you get the base64 encoded key from somewhere
    var base64Key = "+CffHxKmykUvCrrCILd4rZDBcrIoe3w89jnPNXYi0rU="; 
    // convert it to byte[] or alternatively you could store your key as a byte[] 
    //   but that depends on how you set things up.
    var key = Convert.FromBase64String(base64Key);
    var plainText = "EncryptThis";
    var encryptedText = EncryptStringToBase64String(plainText, key);
    var decryptedText = DecryptStringFromBase64String(encryptedText, key);

Here are the encryption methods... EncryptStringToBase64String and DecryptStringFromBase64String.

EDIT: Great point owlstead about using Aes.BlockSize for the IV size. I've also cleaned up the arguement checks.

    private const int KeySize = 256; // in bits
    static string EncryptStringToBase64String(string plainText, byte[] Key)
    {
        // Check arguments. 
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        byte[] returnValue;
        using (var aes = Aes.Create())
        {
            aes.KeySize = KeySize;
            aes.GenerateIV();
            aes.Mode = CipherMode.CBC;
            var iv = aes.IV;
            if (string.IsNullOrEmpty(plainText))
                return Convert.ToBase64String(iv);
            var encryptor = aes.CreateEncryptor(Key, iv);

            // Create the streams used for encryption. 
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                    // this is just our encrypted data
                    var encrypted = msEncrypt.ToArray();
                    returnValue = new byte[encrypted.Length + iv.Length];
                    // append our IV so our decrypt can get it
                    Array.Copy(iv, returnValue, iv.Length);
                    // append our encrypted data
                    Array.Copy(encrypted, 0, returnValue, iv.Length, encrypted.Length);
                }
            }
        }

        // return encrypted bytes converted to Base64String
        return Convert.ToBase64String(returnValue);
    }

    static string DecryptStringFromBase64String(string cipherText, byte[] Key)
    {
        // Check arguments. 
        if (string.IsNullOrEmpty(cipherText))
            return string.Empty;
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");

        string plaintext = null;
        // this is all of the bytes
        var allBytes = Convert.FromBase64String(cipherText);

        using (var aes = Aes.Create())
        {
            aes.KeySize = KeySize;
            aes.Mode = CipherMode.CBC;

            // get our IV that we pre-pended to the data
            byte[] iv = new byte[aes.BlockSize/8];
            if (allBytes.Length < iv.Length)
                throw new ArgumentException("Message was less than IV size.");
            Array.Copy(allBytes, iv, iv.Length);
            // get the data we need to decrypt
            byte[] cipherBytes = new byte[allBytes.Length - iv.Length];
            Array.Copy(allBytes, iv.Length, cipherBytes, 0, cipherBytes.Length);

            // Create a decrytor to perform the stream transform.
            var decryptor = aes.CreateDecryptor(Key, iv);

            // Create the streams used for decryption. 
            using (MemoryStream msDecrypt = new MemoryStream(cipherBytes))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        // Read the decrypted bytes from the decrypting stream 
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }
        }

        return plaintext;
    }

EDIT 2: Never convert actual binary data (like a random key) into a string using a TextEncoding. If data starts life as a string and you convert into binary using an encoding then and ONLY then can you convert it from binary into a string using the proper encoding. Otherwise you will have code that works sometimes which is a recipe for torturing yourself.

        // This is base64 not UTF8, unicode, ASCII or anything else!!!
        string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
        aesKey = Convert.FromBase64String(sKey);

Edit 3:

Why use File.WriteAllText to write the file but use File.ReadAllBytes when you read it? You can write it and read it as text and use ASCII encoding since base64 is guaranteed to be ASCII. Also Decrypt returns a decrypted string which you are not storing or using. The decrypted string is what you need to parse because it's your xml.

You can use this for saving the file...

    var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());

In your decrypt you should do this...

    var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
    string xmlStr = DecryptStringFromBase64String(encryptedStr , keyBytes);

EDIT 4: I've attempted to duplicate your exception and I can't make it happen... here is my test code that I'm running in a console app and it works.

public static void EncryptMethod()
{
    var fileName = @"c:/text.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);

    var sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
    var aesKey = Convert.FromBase64String(sKey);

    string encyptedText = EncryptStringToBase64String(doc.ToString(), aesKey);
    File.WriteAllText(fileName, encyptedText);
}


public static void DecryptMethod()
{
    var fileName = @"c:/text.xml";
    string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
    Byte[] keyBytes = Convert.FromBase64String(sKey);

    var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
    string xmlStr = DecryptStringFromBase64String(encryptedText, keyBytes);

    using (XmlReader reader = XmlReader.Create(new StringReader(xmlStr)))
    {
        reader.ReadToFollowing("DatabaseServerName");
        Console.WriteLine(reader.ReadElementContentAsString());
        reader.ReadToFollowing("DatabaseUserName");
        Console.WriteLine(reader.ReadElementContentAsString());
        reader.ReadToFollowing("DatabasePassword");
        Console.WriteLine(reader.ReadElementContentAsString());
        reader.ReadToFollowing("RegistrationCode");
        Console.WriteLine(reader.ReadElementContentAsString());
    }
}

Usage from the console app...

    EncryptMethod();
    DecryptMethod();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top