如何在一个程序中使用AES加密,在另一个程序中解密

我被告知不要使用 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(value, password); } public static string Encrypt(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(value, password); } public static string Decrypt(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 []传递给加密函数。 所以我尝试使用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); 

编辑2:这是现在的两个部分。 加密方:

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

我在下面写的算法使用一个随机的初始化向量,它放在加密值的开头,这样你可以加密相同的值两次,而不是得到相同的加密输出。 这是相当正常的,让你只来回传递一个“秘密”。

您需要通过一些越界过程来共享您的密钥,因为加密和解密都需要知道密钥。 这是其他地方记录的密钥交换的单独主题。 这是一个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大小的问题很棒。 我还清理了辩论检查。

  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将实际二进制数据(如随机密钥)转换为字符串。 如果数据以字符串forms开始生命并且您使用编码转换为二进制,那么只能使用正确的编码将其从二进制转换为字符串。 否则你将拥有有时可以使用的代码,这是一种折磨自己的方法。

  // 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:我试图复制你的exception,我无法实现它...这是我的测试代码,我在控制台应用程序中运行,它的工作原理。

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