C# 尝试获取node.js中字符串的AES加密以匹配.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

我正在尝试加密node.js中的一个值,该值可以在.net中解密。我已经得到了他们在.net端用于加密值的代码,我正试图在node.js脚本中实现相同的加密值

我绝对不是一个加密爱好者,所以请帮我找出哪里出了问题。我的node.js加密值与.net加密值不匹配,而且我的node.js加密值实际上也不是每次运行脚本时都返回相同的值

以下是.net加密逻辑:

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