问题
I'm trying to encrypt a value in node.js that I can decrypt in .net. I've been given the code that they use on the .net side of things for encrypting a value and i'm trying to achieve the same encrypted value in my node.js script.
I'm definitely not an encryption buff so please help me figure out where i'm going wrong. My node.js encrypted value is not matching that of the .net encrypted value, and my node.js encrypted value is actually not returning the same value every time I run the script either.
Here's the .net encryption logic:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("start:");
string key = "mysecretkey";
string secret = "encryptThisMessage";
string crypto = EncryptString(secret, key);
Console.WriteLine(crypto);
string returnValue = DecryptString(crypto, key);
Console.WriteLine(returnValue);
Console.ReadKey();
}
/// <summary>
/// Encrpyts the sourceString, returns this result as an Aes encrpyted, BASE64 encoded string
/// </summary>
/// <param name="plainSourceStringToEncrypt">a plain, Framework string (ASCII, null terminated)</param>
/// <param name="passPhrase">The pass phrase.</param>
/// <returns>
/// returns an Aes encrypted, BASE64 encoded string
/// </returns>
public static string EncryptString(string plainSourceStringToEncrypt, string passPhrase)
{
//Set up the encryption objects
using (AesCryptoServiceProvider acsp = GetProvider(Encoding.Default.GetBytes(passPhrase)))
{
byte[] sourceBytes = Encoding.ASCII.GetBytes(plainSourceStringToEncrypt);
ICryptoTransform ictE = acsp.CreateEncryptor();
//Set up stream to contain the encryption
MemoryStream msS = new MemoryStream();
//Perform the encrpytion, storing output into the stream
CryptoStream csS = new CryptoStream(msS, ictE, CryptoStreamMode.Write);
csS.Write(sourceBytes, 0, sourceBytes.Length);
csS.FlushFinalBlock();
//sourceBytes are now encrypted as an array of secure bytes
byte[] encryptedBytes = msS.ToArray(); //.ToArray() is important, don't mess with the buffer
//return the encrypted bytes as a BASE64 encoded string
return Convert.ToBase64String(encryptedBytes);
}
}
/// <summary>
/// Decrypts a BASE64 encoded string of encrypted data, returns a plain string
/// </summary>
/// <param name="base64StringToDecrypt">an Aes encrypted AND base64 encoded string</param>
/// <param name="passphrase">The passphrase.</param>
/// <returns>returns a plain string</returns>
public static string DecryptString(string base64StringToDecrypt, string passphrase)
{
//Set up the encryption objects
using (AesCryptoServiceProvider acsp = GetProvider(Encoding.Default.GetBytes(passphrase)))
{
byte[] RawBytes = Convert.FromBase64String(base64StringToDecrypt);
ICryptoTransform ictD = acsp.CreateDecryptor();
//RawBytes now contains original byte array, still in Encrypted state
//Decrypt into stream
MemoryStream msD = new MemoryStream(RawBytes, 0, RawBytes.Length);
CryptoStream csD = new CryptoStream(msD, ictD, CryptoStreamMode.Read);
//csD now contains original byte array, fully decrypted
//return the content of msD as a regular string
return (new StreamReader(csD)).ReadToEnd();
}
}
private static AesCryptoServiceProvider GetProvider(byte[] key)
{
AesCryptoServiceProvider result = new AesCryptoServiceProvider();
result.BlockSize = 128;
result.KeySize = 128;
result.Mode = CipherMode.CBC;
result.Padding = PaddingMode.PKCS7;
result.GenerateIV();
result.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
byte[] RealKey = GetKey(key, result);
result.Key = RealKey;
// result.IV = RealKey;
return result;
}
private static byte[] GetKey(byte[] suggestedKey, SymmetricAlgorithm p)
{
byte[] kRaw = suggestedKey;
List<byte> kList = new List<byte>();
for (int i = 0; i < p.LegalKeySizes[0].MinSize; i += 8)
{
kList.Add(kRaw[(i / 8) % kRaw.Length]);
}
byte[] k = kList.ToArray();
return k;
}
}
}
My node.js script:
var crypto = require('crypto-js');
var key = "mysecretkey";
var secret = "encryptThisMessage";
e1 = crypto.AES.encrypt(secret, key, {mode: crypto.mode.CBC, padding: crypto.pad.Pkcs7});
e2 = crypto.AES.encrypt(secret, key, {mode: crypto.mode.CBC, padding: crypto.pad.Pkcs7});
console.log('e1');
console.log(crypto.enc.Hex.stringify(e1));
console.log(e1.toString());
console.log(e1.salt.toString());
console.log(e1.iv.toString());
console.log(e1.ciphertext.toString());
console.log(e1.ciphertext.toString(crypto.enc.Base64));
console.log('e2');
console.log(e2.toString());
console.log(e2.salt.toString());
console.log(e2.iv.toString());
console.log(e2.ciphertext.toString(crypto.enc.Base64));
When running the encryption piece in the c# code, the value looks like this: (slightly modified for security purposes) dp+8cjr/ajEw5oePdiG+4g==
. How can I change my node.js code to output this matched encrypted value?
Output of node.js script:

回答1:
You are mixing apples and oranges.
When you pass a string as key to CryptoJS, it derives a key and iv that it uses for the decryption. The string is treated as a passphrase, which is salted. Run this code a couple of times in node.js:
var key = "mysecretkey";
var secret = "encryptThisMessage";
e1 = crypto.AES.encrypt(secret, key, {mode: crypto.mode.CBC, padding: crypto.pad.Pkcs7});
console.log("key: " + crypto.enc.Base64.stringify(e1.key));
console.log("iv: " + crypto.enc.Base64.stringify(e1.iv));
console.log("salt: " + crypto.enc.Base64.stringify(e1.salt));
console.log("ciphertext: " + crypto.enc.Base64.stringify(e1.ciphertext));
p = crypto.AES.decrypt(e1, key, {mode: crypto.mode.CBC, padding: crypto.pad.Pkcs7});
console.log("decrypted: " + crypto.enc.Utf8.stringify(p));
Note that it produces different keys and IVs each time, yet it always decrypts back to the original (because e1 carries the salt which lets decrypt derive the same key). Check out this documentation for CryptoJS here.
In your C# code, you are always using the same key and IV. These don't match the key and IV in CryptoJS. Try this code which exactly matches the key and IV that your C# code produces:
var key = crypto.enc.Base64.parse('bXlzZWNyZXRrZXlteXNlYw=='); // Matching C# code's key
var iv = crypto.enc.Base64.parse('AAAAAAAAAAAAAAAAAAAAAA=='); // 16 ZERO bytes, same as C# code
var secret = "encryptThisMessage";
e1 = crypto.AES.encrypt(secret, key, {iv: iv, mode: crypto.mode.CBC, padding: crypto.pad.Pkcs7});
console.log("ciphertext: " + crypto.enc.Base64.stringify(e1.ciphertext));
Note that this time I am not passing CryptoJS a string for the key, to be interpreted as a passphrase, but rather, a CryptoJS word array to be interpreted directly as key bytes. Also, I am passing the IV in the params.
That last bit of code produces the same ciphertext as your C# code. I'm using Base64 here for the key and IV as a convenient shortcut to create word arrays. Use the same key and IV on both ends and it will work.
EDIT:
I thought it interesting to show the CryptoJS -- i.e. OpenSSL -- key derivation in code, so that instead of having CryptoJS match C#, have C# match CryptoJS.
OpenSSL key derivation is described here.
This derives the key and IV -- kept simple for clarity:
public byte[] Derive48(string passphrase, byte[] salt)
{
using (var md5 = new MD5CryptoServiceProvider())
{
var source = Encoding.UTF8.GetBytes(passphrase).Concat(salt).ToArray();
var data = md5.ComputeHash(source);
var output = data;
while (output.Length < 48)
{
data = md5.ComputeHash(data.Concat(source).ToArray());
output = output.Concat(data).ToArray();
}
return output.Take(48).ToArray();
}
}
You can use it like this:
string key = "mysecretkey";
string secret = "encryptThisMessage";
byte[] salt = Convert.FromBase64String("zTEeMVPN2eY=");
string crypto = EncryptString(secret, key, salt);
Console.WriteLine(crypto);
string returnValue = DecryptString(crypto, key, salt);
Console.WriteLine(returnValue);
...
public string EncryptString(string plainSourceStringToEncrypt, string passPhrase, byte[] salt)
{
//Set up the encryption objects
using (AesCryptoServiceProvider acsp = GetProvider(passPhrase, salt))
{
...
private AesCryptoServiceProvider GetProvider(string passphrase, byte[] salt)
{
AesCryptoServiceProvider result = new AesCryptoServiceProvider();
result.BlockSize = 128;
result.KeySize = 128;
result.Mode = CipherMode.CBC;
result.Padding = PaddingMode.PKCS7;
var derived = this.Derive48(passphrase, salt);
result.Key = derived.Take(32).ToArray();
result.IV = derived.Skip(32).Take(16).ToArray();
return result;
}
来源:https://stackoverflow.com/questions/24492467/trying-to-get-aes-encryption-of-a-string-in-node-js-to-match-encrypted-value-in