【问题标题】:Trying to get AES encryption of a string in node.js to match encrypted value in .net尝试对 node.js 中的字符串进行 AES 加密以匹配 .net 中的加密值
【发布时间】:2014-08-20 23:06:30
【问题描述】:

我正在尝试加密 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;
        }
    }
}

我的 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));

在 c# 代码中运行加密部分时,值如下所示:(出于安全目的稍作修改)dp+8cjr/ajEw5oePdiG+4g==如何更改我的 node.js 代码以输出此匹配的加密值?

node.js 脚本的输出:

【问题讨论】:

  • 您完全缺少 C# 代码中的密钥派生部分。即使明文相同,密文应该看起来是随机的。这可能是由于每次运行使用不同的盐。
  • 你能详细说明(也许在答案而不是评论中)?我没有写 c# 部分,也没有完全理解它。
  • 尝试以this为起点。
  • 那么最终是c#实现有问题,还是只是我没有在node.js版本中使用所有适当的设置/配置?
  • 视情况而定,如果要使用密码,请更改 C# 实现。要使用键来实现它,请将显式 IV 添加到 NodeJS 实现中。

标签: c# javascript node.js encryption cryptography


【解决方案1】:

你在混合苹果和橙子

当您将字符串作为密钥传递给 CryptoJS 时,它会派生一个用于解密的密钥和 iv。该字符串被视为密码短语,它是salted。在 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));

请注意,它每次都会产生不同的密钥和 IV,但它总是会解密回原来的值(因为 e1 带有盐,它可以让解密派生相同的密钥)。查看 CryptoJS here 的此文档。

在您的C# 代码 中,您始终使用相同的键和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 密钥派生描述为here

这会导出密钥和 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();
    }
}

你可以这样使用它:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-27
    • 2013-06-22
    • 2011-08-24
    • 2018-01-09
    • 1970-01-01
    • 2015-11-01
    • 2013-07-20
    • 1970-01-01
    相关资源
    最近更新 更多