在NodeJS和C#中AES256encryption/解密

我对以下问题的结果有了一些自由:

  • AES在.NET中encryption并使用Node.jsencryption解密?
  • 在.NET中解密来自node.js的AES256encryption数据 – 如何从密码中获得IV和Key
  • C#版本的OpenSSL EVP_BytesToKey方法?

并创build了以下类文件…

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace T1.CoreUtils.Utilities { public static class CryptoUtility { public static string Encrypt(string input, string passphrase = null) { byte[] key, iv; DeriveKeyAndIV(Encoding.ASCII.GetBytes(passphrase), null, 1, out key, out iv); return Convert.ToBase64String(EncryptStringToBytes(input, key, iv)); } public static string Decrypt(string inputBase64, string passphrase = null) { byte[] key, iv; DeriveKeyAndIV(Encoding.ASCII.GetBytes(passphrase), null, 1, out key, out iv); return DecryptStringFromBytes(Convert.FromBase64String(inputBase64), key, iv); } private static void DeriveKeyAndIV(byte[] data, byte[] salt, int count, out byte[] key, out byte[] iv) { List<byte> hashList = new List<byte>(); byte[] currentHash = new byte[0]; int preHashLength = data.Length + ((salt != null) ? salt.Length : 0); byte[] preHash = new byte[preHashLength]; System.Buffer.BlockCopy(data, 0, preHash, 0, data.Length); if (salt != null) System.Buffer.BlockCopy(salt, 0, preHash, data.Length, salt.Length); MD5 hash = MD5.Create(); currentHash = hash.ComputeHash(preHash); for (int i = 1; i < count; i++) { currentHash = hash.ComputeHash(currentHash); } hashList.AddRange(currentHash); while (hashList.Count < 48) // for 32-byte key and 16-byte iv { preHashLength = currentHash.Length + data.Length + ((salt != null) ? salt.Length : 0); preHash = new byte[preHashLength]; System.Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length); System.Buffer.BlockCopy(data, 0, preHash, currentHash.Length, data.Length); if (salt != null) System.Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + data.Length, salt.Length); currentHash = hash.ComputeHash(preHash); for (int i = 1; i < count; i++) { currentHash = hash.ComputeHash(currentHash); } hashList.AddRange(currentHash); } hash.Clear(); key = new byte[32]; iv = new byte[16]; hashList.CopyTo(0, key, 0, 32); hashList.CopyTo(32, iv, 0, 16); } static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV) { // Check arguments. if (plainText == null || plainText.Length <= 0) throw new ArgumentNullException("plainText"); if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key"); if (IV == null || IV.Length <= 0) throw new ArgumentNullException("Key"); byte[] encrypted; // Create an RijndaelManaged object // with the specified key and IV. using (RijndaelManaged rijAlg = new RijndaelManaged()) { rijAlg.Key = Key; rijAlg.IV = IV; // Create a decrytor to perform the stream transform. ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.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); } encrypted = msEncrypt.ToArray(); } } } // Return the encrypted bytes from the memory stream. return encrypted; } static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV) { // Check arguments. if (cipherText == null || cipherText.Length <= 0) throw new ArgumentNullException("cipherText"); if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key"); if (IV == null || IV.Length <= 0) throw new ArgumentNullException("Key"); // Declare the string used to hold // the decrypted text. string plaintext = null; // Create an RijndaelManaged object // with the specified key and IV. using (RijndaelManaged rijAlg = new RijndaelManaged()) { rijAlg.Key = Key; rijAlg.IV = IV; // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); // Create the streams used for decryption. using (MemoryStream msDecrypt = new MemoryStream(cipherText)) { 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; } } } 

从这里,我通过节点生成了以下内容:

 var crypto = require('crypto'); var input = "This is î╥≤ what it is."; var passkey= "This is my password."; var cipher = crypto.createCipher('aes-256-cbc', passkey); var encrypted = cipher.update(input, 'utf8', 'base64') + cipher.final('base64'); encrypted // '9rTbNbfJkYVE2m5d8g/8b/qAfeCU9rbk09Na/Pw0bak=' input = "I am the walrus, coo coo cachoo!"; passkey = "I am a ≥ò'ÿ boy baby!"; cipher = crypto.createCipher('aes-256-cbc', passkey); encrypted = cipher.update(input, 'utf8', 'base64') + cipher.final('base64'); // 'j/e+f5JU5yerSvO7FBJzR1tGro0Ie3L8sWYaupRW1JJhraGqBfQ9z+h85VhSzEjD' var decipher = crypto.createDecipher('aes-256-cbc', passkey); var plain = decipher.update(encrypted, 'base64', 'utf8') + decipher.final('utf8'); plain // 'I am the walrus, coo coo cachoo!' 

由此,我创build了以下testing用例:

 using System; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace T1.CoreUtils.Test.Utilities.Tests { [TestClass] public class UnitTest1 { [TestMethod] public void EncryptReturnsExpectedValue1_unicode_in_plaintext() { var passkey = "This is my password."; var plain = "This is î╥≤ what it is."; var encrypted = "9rTbNbfJkYVE2m5d8g/8b/qAfeCU9rbk09Na/Pw0bak="; var actual = T1.CoreUtils.Utilities.CryptoUtility.Encrypt(plain, passkey); Assert.AreEqual(encrypted, actual); } [TestMethod] public void EncryptReturnsExpectedValue2_unicode_in_passkey() { var passkey = "I am a ≥ò'ÿ boy baby!"; var plain = "I am the walrus, coo coo cachoo!"; var encrypted = "j/e+f5JU5yerSvO7FBJzR1tGro0Ie3L8sWYaupRW1JJhraGqBfQ9z+h85VhSzEjD"; var actual = T1.CoreUtils.Utilities.CryptoUtility.Encrypt(plain, passkey); Assert.AreEqual(encrypted, actual); } [TestMethod] public void DecryptReturnsExpectedValue1() { var passkey = "This is my password."; var plain = "This is î╥≤ what it is."; var encrypted = "9rTbNbfJkYVE2m5d8g/8b/qAfeCU9rbk09Na/Pw0bak="; var actual = T1.CoreUtils.Utilities.CryptoUtility.Decrypt(encrypted, passkey); Assert.AreEqual(plain, actual); } [TestMethod] public void DecryptReturnsExpectedValue2() { var passkey = "I am a ≥ò'ÿ boy baby!"; var plain = "I am the walrus, coo coo cachoo!"; var encrypted = "j/e+f5JU5yerSvO7FBJzR1tGro0Ie3L8sWYaupRW1JJhraGqBfQ9z+h85VhSzEjD"; var actual = T1.CoreUtils.Utilities.CryptoUtility.Decrypt(encrypted, passkey); Assert.AreEqual(plain, actual); } } } 

关:

  • EncryptReturnsExpectedValue1_unicode_in_plaintext
  • DecryptReturnsExpectedValue1

失败:

  • EncryptReturnsExpectedValue2_unicode_in_passkey
  • DecryptReturnsExpectedValue2

我只能猜测这个问题是在DeriveKeyAndIV方法中。 将尝试一些不同的方法,并回答如果我自己find它。

好的,在检查node.js源代码时,我确定编码是使用一个新的Buffer(passkey,'binary'),它只使用原始值x和0xFF作为所使用的字节,所以我创build了一个匹配方法在C#中…这里是有问题的方法…

 private static byte[] RawBytesFromString(string input) { var ret = new List<Byte>(); foreach (char x in input) { var c = (byte)((ulong)x & 0xFF); ret.Add(c); } return ret.ToArray(); } 

并更新/工作CryptoUtil.cs

 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace T1.CoreUtils.Utilities { public static class CryptoUtility { /* Wanting to stay compatible with NodeJS * http://stackoverflow.com/questions/18502375/aes256-encryption-decryption-in-both-nodejs-and-c-sharp-net/ * http://stackoverflow.com/questions/12261540/decrypting-aes256-encrypted-data-in-net-from-node-js-how-to-obtain-iv-and-key * http://stackoverflow.com/questions/8008253/c-sharp-version-of-openssl-evp-bytestokey-method * * var cipher = crypto.createCipher('aes-256-cbc', 'passphrase'); * var encrypted = cipher.update("test", 'utf8', 'base64') + cipher.final('base64'); * * var decipher = crypto.createDecipher('aes-256-cbc', 'passphrase'); * var plain = decipher.update(encrypted, 'base64', 'utf8') + decipher.final('utf8'); */ public static string Encrypt(string input, string passphrase = null) { byte[] key, iv; DeriveKeyAndIV(RawBytesFromString(passphrase), null, 1, out key, out iv); return Convert.ToBase64String(EncryptStringToBytes(input, key, iv)); } public static string Decrypt(string inputBase64, string passphrase = null) { byte[] key, iv; DeriveKeyAndIV(RawBytesFromString(passphrase), null, 1, out key, out iv); return DecryptStringFromBytes(Convert.FromBase64String(inputBase64), key, iv); } private static byte[] RawBytesFromString(string input) { var ret = new List<Byte>(); foreach (char x in input) { var c = (byte)((ulong)x & 0xFF); ret.Add(c); } return ret.ToArray(); } private static void DeriveKeyAndIV(byte[] data, byte[] salt, int count, out byte[] key, out byte[] iv) { List<byte> hashList = new List<byte>(); byte[] currentHash = new byte[0]; int preHashLength = data.Length + ((salt != null) ? salt.Length : 0); byte[] preHash = new byte[preHashLength]; System.Buffer.BlockCopy(data, 0, preHash, 0, data.Length); if (salt != null) System.Buffer.BlockCopy(salt, 0, preHash, data.Length, salt.Length); MD5 hash = MD5.Create(); currentHash = hash.ComputeHash(preHash); for (int i = 1; i < count; i++) { currentHash = hash.ComputeHash(currentHash); } hashList.AddRange(currentHash); while (hashList.Count < 48) // for 32-byte key and 16-byte iv { preHashLength = currentHash.Length + data.Length + ((salt != null) ? salt.Length : 0); preHash = new byte[preHashLength]; System.Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length); System.Buffer.BlockCopy(data, 0, preHash, currentHash.Length, data.Length); if (salt != null) System.Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + data.Length, salt.Length); currentHash = hash.ComputeHash(preHash); for (int i = 1; i < count; i++) { currentHash = hash.ComputeHash(currentHash); } hashList.AddRange(currentHash); } hash.Clear(); key = new byte[32]; iv = new byte[16]; hashList.CopyTo(0, key, 0, 32); hashList.CopyTo(32, iv, 0, 16); } static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV) { // Check arguments. if (plainText == null || plainText.Length <= 0) throw new ArgumentNullException("plainText"); if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key"); if (IV == null || IV.Length <= 0) throw new ArgumentNullException("Key"); byte[] encrypted; // Create an RijndaelManaged object // with the specified key and IV. using (RijndaelManaged cipher = new RijndaelManaged()) { cipher.Key = Key; cipher.IV = IV; //cipher.Mode = CipherMode.CBC; //cipher.Padding = PaddingMode.PKCS7; // Create a decrytor to perform the stream transform. ICryptoTransform encryptor = cipher.CreateEncryptor(cipher.Key, cipher.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); } encrypted = msEncrypt.ToArray(); } } } // Return the encrypted bytes from the memory stream. return encrypted; } static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV) { // Check arguments. if (cipherText == null || cipherText.Length <= 0) throw new ArgumentNullException("cipherText"); if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key"); if (IV == null || IV.Length <= 0) throw new ArgumentNullException("Key"); // Declare the string used to hold // the decrypted text. string plaintext = null; // Create an RijndaelManaged object // with the specified key and IV. using (var cipher = new RijndaelManaged()) { cipher.Key = Key; cipher.IV = IV; //cipher.Mode = CipherMode.CBC; //cipher.Padding = PaddingMode.PKCS7; // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = cipher.CreateDecryptor(cipher.Key, cipher.IV); // Create the streams used for decryption. using (MemoryStream msDecrypt = new MemoryStream(cipherText)) { 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; } } } 

注意:一些更多的代码与此有关…

这些都不是nugetnpm因为他们真的不属于那里……主要是想法和参考。 我确实需要更好地清理节点,以便匹配。