C# 尝试获取node.js中字符串的AES加密以匹配.net中的加密值
我正在尝试加密node.js中的一个值,该值可以在.net中解密。我已经得到了他们在.net端用于加密值的代码,我正试图在node.js脚本中实现相同的加密值 我绝对不是一个加密爱好者,所以请帮我找出哪里出了问题。我的node.js加密值与.net加密值不匹配,而且我的node.js加密值实际上也不是每次运行脚本时都返回相同的值 以下是.net加密逻辑:C# 尝试获取node.js中字符串的AES加密以匹配.net中的加密值,c#,javascript,node.js,encryption,cryptography,C#,Javascript,Node.js,Encryption,Cryptography,我正在尝试加密node.js中的一个值,该值可以在.net中解密。我已经得到了他们在.net端用于加密值的代码,我正试图在node.js脚本中实现相同的加密值 我绝对不是一个加密爱好者,所以请帮我找出哪里出了问题。我的node.js加密值与.net加密值不匹配,而且我的node.js加密值实际上也不是每次运行脚本时都返回相同的值 以下是.net加密逻辑: using System; using System.Collections.Generic; using System.Linq; usin
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;
}
}
}
在c#代码中运行加密块时,该值如下所示:(出于安全目的稍微修改)dp+8cjr/ajEw5oePdiG+4g=
如何更改node.js代码以输出匹配的加密值?
node.js脚本的输出:
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));
你在混合苹果和橙子 当您将一个字符串作为密钥传递给CryptoJS时,它将派生一个密钥和一个用于解密的iv。字符串被视为密码短语,即盐渍的。在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));
请注意,它每次都会生成不同的密钥和IVs,但它总是解密回原始密钥(因为e1携带salt,它让decrypt派生相同的密钥)。请查看此CryptoJS文档
在C#code中,您总是使用相同的键和IV。这些与CryptoJS中的密钥和IV不匹配。请尝试与C代码生成的密钥和IV完全匹配的代码:
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));
请注意,这一次我没有为密钥传递CryptoJS字符串,将其解释为密码短语,而是将CryptoJS字数组直接解释为密钥字节。此外,我正在通过参数中的IV
最后一位代码产生与C代码相同的密文。我在这里使用Base64作为键,使用IV作为创建单词数组的便捷快捷方式。使用相同的键和两端的IV,它将工作
编辑:
我认为在代码中显示CryptoJS(即OpenSSL)密钥派生很有趣,这样就不用让CryptoJS匹配C,而是让C匹配CryptoJS
描述了OpenSSL密钥派生
这衍生出键和IV——为了清晰起见保持简单:
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();
}
}
您完全缺少C代码中的关键派生部分。即使明文相同,密文也应该看起来是随机的。这可能是因为每次跑步都有不同的盐。你能详细说明一下(可能是回答而不是评论)吗?我没有写c#文章,也没有完全理解它。试着用它作为起点。那么c#实现最终是否有问题,或者只是我没有在node.js版本中使用所有适当的设置/配置?这取决于,如果你想使用密码,请更改c#实现。要使用键实现它,请向NodeJS实现中添加显式IV。
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;
}