【问题标题】:How do you use AES to Encrypt in One Program, Decrypt in Another您如何使用 AES 在一个程序中加密,在另一个程序中解密
【发布时间】:2014-04-07 18:33:12
【问题描述】:

有人告诉我not to use RSA 加密简单文本但使用 AES。我找到了一段简单的代码来实现 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);
        }

    }

但是,这是基于返回的字符串,然后用于在同一程序中解密。我需要在 WinForms 程序中加密以下数据并在整个单独的 Windows 服务程序中解密:

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

我该怎么做?请显示示例代码。

编辑:凯文,我已经实现了你的算法,但问题是我想生成一次密钥并将其保存以用于其他程序进行解密,但我需要将 byte[] 传递给 encrypt 函数。所以我尝试使用 System.Text.Encoding.ASCII.GetBytes(key);它没有正确地做到这一点。我的 byte[] 字节数错误。

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:这两个部分都是现在的样子。加密方面:

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

尝试解密的 Windows 服务端:

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

【问题讨论】:

  • 您需要存储盐和初始化向量,以便您可以在两个应用程序上使用它。你如何做到这一点取决于你,例如文件,配置文件,数据库,硬编码。一个更好的主意是使用证书的密钥来加密和解密。这样您就不必担心密钥存储
  • 你能用代码展示一个例子吗?我只是不明白这些东西,很困惑!
  • 我的建议确实暗示使用 RSA,只是因为这解决了密钥存储问题,所以可能对你不起作用,抱歉,我错过了顶部的链接。看看 this for storage of key and iv (stackoverflow.com/questions/18324149/…) 本质上,您需要将密钥和 IV 存储在某个地方,以便 winform 应用程序和 Windows 服务都可以读取它们
  • 加密类型的选择——对称或非对称(或者,实际上是混合)——更多地取决于密钥管理而不是易用性。仅仅因为它简单而选择一些功能将导致不安全的解决方案。您至少需要对您正在做什么有一个提示,然后选择一个经过严格审查的解决方案。
  • @user2471435 永远不要使用 TextEncoding 将字节转换为字符串,除非您知道它以相同的方式编码为字节。您必须使用 Convert.FromBase64String(sKey)。我将编辑我的答案。

标签: c# encryption cryptography aes


【解决方案1】:

我在下面编写的算法使用了一个随机初始化向量,它放在加密值的开头,因此您可以对相同的值进行两次加密,而不会得到相同的加密输出。这很正常,只允许你来回传递一个“秘密”。

您将需要通过一些越界过程共享您的密钥,因为加密和解密都需要知道密钥。这是在其他地方记录的密钥交换的单独主题。 Here 是一个 SO 链接,如果您需要一些帮助,可以帮助您入门。

此外,如果您要“编造”随机值,我建议您不要这样做。使用以下内容来帮助您生成随机字节,然后将它们转换为更易于人类使用或某些类型的密钥交换的 base64 字符串。请注意,这只是您如何生成随机密钥的示例......实际上,这可能基于一些可重新创建的用户输入,或者您使用用户哈希值来查找您生成的随机密钥。无论如何,这里是钥匙的代码......

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

用法如下...

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

这里是加密方法...EncryptStringToBase64StringDecryptStringFromBase64String

编辑:关于使用 Aes.BlockSize 作为 IV 大小的要点 owlstead。我还清理了争论检查。

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

编辑 2:永远不要使用 TextEncoding 将实际的二进制数据(如随机密钥)转换为字符串。 如果数据以字符串的形式开始,并且您使用编码转换为二进制,那么并且仅在那时您可以使用正确的编码将其从二进制转换为字符串吗?否则,您将获得有时有效的代码,这是折磨自己的秘诀。

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

编辑 3:

为什么在写入文件时使用File.WriteAllText 而读取文件时使用File.ReadAllBytes?您可以将其作为文本编写和读取,并使用 ASCII 编码,因为 base64 保证为 ASCII。 Decrypt 还会返回一个您未存储或使用的解密字符串。解密后的字符串是您需要解析的,因为它是您的 xml。

您可以使用它来保存文件...

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

在你的解密中你应该这样做......

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

编辑 4: 我试图复制您的异常,但无法实现...这是我在控制台应用程序中运行的测试代码,它可以工作。

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

从控制台应用程序使用...

    EncryptMethod();
    DecryptMethod();

【讨论】:

  • 回答比较OK。不过有一些注意事项: - IV 的大小应始终为Aes.Blocksize(并且块大小不是密钥大小的函数)。明文可能为空,因此检查== 0 没有意义(当然更不用说&lt; 0)。将纯文本分配给 null 也没有太大区别。
  • 您调用 Convert.ToBase64String(aes.Key) 但 Encrypt 和 Decrypt 使用 Byte[] 作为密钥。我到底如何得到它并存储它?
  • @user2471435 aes.Key 是一个字节[]。如果您需要阅读它,请将其转换为 base64 ......但如果不需要,只需将其作为密钥传递。我将编辑我的答案以反映使用情况。
  • Kevin,我会在编辑中向您展示我正在尝试做的事情。我想进行一次加密并存储字符串,然后从中获取字节 [],但我的代码错误。我会发布它。
  • @owlstead 我的代码使用数组之前是其中一些检查的来源。我已经纠正了它,现在也使用 Aes.BlockSize 了!
猜你喜欢
  • 2018-08-30
  • 2019-11-09
  • 1970-01-01
  • 2020-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多