在Node JS和C#中进行AES加密会得到不同的结果

7

我有一个使用案例,需要使用AES 256算法对文本进行编码并发送。客户端代码是用C#编写的,将解密该代码。

JS中的加密代码:

const crypto = require('crypto');
  algorithm = 'aes-256-cbc',
  secret = '1234567890123456',
  keystring = crypto.createHash('sha256').update(String(secret)).digest('base64').substr(0, 16);
  iv = crypto.createHash('sha256').update(String(secret)).digest('base64').substr(0, 16);
  inputEncoding = 'utf8',
  outputEncoding = 'base64';


function encrypt(text) {
  let cipher = crypto.createCipheriv('aes-256-cbc', keystring, iv);
  let encrypted = cipher.update(text, inputEncoding, outputEncoding)
  encrypted += cipher.final(outputEncoding);
  return encrypted;
}

客户端使用的代码已更新:

var keybytes = Encoding.UTF8.GetBytes(passwordKey);
var iv = Encoding.UTF8.GetBytes(passwordKey);

private byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] iv)
        {
            try
            {
                // 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 a RijndaelManaged object  
                // with the specified key and IV.  
                using (var rijAlg = new RijndaelManaged())
                {
                    rijAlg.Mode = CipherMode.CBC;
                    rijAlg.Padding = PaddingMode.PKCS7;
                    rijAlg.FeedbackSize = 128;

                    rijAlg.Key = key;
                    rijAlg.IV = iv;

                    // Create a decrytor to perform the stream transform.  
                    var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

                    // Create the streams used for encryption.  
                    using (var msEncrypt = new MemoryStream())
                    {
                        using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                        {
                            using (var 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;
            }
            catch (Exception ex)
            {
                throw ex;
                //LoggerCS.logError("Utility", "EncryptStringToBytes", JsonConvert.SerializeObject(null), ex.ToString(), ex.StackTrace);
            }
            return null;
        }

在C#中使用的keyString和IV值相同,并使用Utf8进行加密。想要在Node JS中找到相应的操作。


点赞给这个小提琴,做得好,祝你好运。 - TheGeneral
1
你为什么忽略了“algorithm”,硬编码成了“aes-128-cbc”? - ProgrammingLlama
@John - 没有什么特别的原因,我只是想看看其他用户在类似问题上的建议,并尝试了aes-128。 - Suchetha B
1
我只是想知道那是否您的问题,因为您的C#代码使用了AES-256加密。 - ProgrammingLlama
@Topaco - 我有点困惑。C#使用块大小为256。如果必须将其更改为128以使其成为AES-256算法,则在我的情况下不可行。 C#代码已经生产了很长时间,不能被修改。只能以这样的方式实现Node JS实现,以便与C#同步。 - Suchetha B
显示剩余3条评论
4个回答

3

TLDR;

你在使用不同的IV和算法(AES-128与AES-256),因此会得到不同的结果... 如果要获得相同的结果,你需要使用相同的IV、密钥和算法。但这是一种反模式(即不应该这样做)!请查看约翰的评论,他指出你在代码中忽略了算法变量,这和不同的IV一起导致了你得到不同的结果。

更详细的回答:

1)实际上,你希望使用相同的密钥加密相同的消息(明文)不能始终产生相同的加密结果(密文)。否则,任何窃听者都会知道重复的消息何时再次发送。

2)初始化向量(IV)用于提供随机性,以使使用给定密钥时,相同的明文不总是产生相同的密文。

3)这意味着,为了解密消息,你不仅需要知道密钥,还需要知道IV。

4)IV应该是随机的,而不是从密钥确定性派生出来的,否则每次使用相同的密钥都会有相同的IV,因此每次相同的明文加密将产生相同的密文。(容易受到窃听者观察给定消息接收结果并开始确定消息意义的影响)。

更多信息,请参阅AES Encryption - Key versus IV这个问题的答案和维基百科中初始化向量的词条。


在这种情况下,密钥和IV值将相同,因为我正在尝试匹配使用相同字符串生成它们的C#代码。 - Suchetha B
1
当你在调试代码时,将它们设置为相同的是可以的。但是当你开始在生产环境中使用时,请注意每次加密都应该使用随机IV。 - Alex Hague

0
整个问题很可能与编码有关。.Net加密类默认使用Unicode或utf16-le编码。而您已经指定了base64作为输出编码方式。 将其更改为utf16le即可。 例如:
outputEncoding = 'utf16le';

在这里查看Node Js文档


0

试试这个:

var crypto = require('text');

var mykey = crypto.createCipher('aes-256-cbc',  keystring, iv);
var mystr = mykey.update('abc', 'utf8', 'hex')
mystr += mykey.final('hex');

console.log(mystr);

我只能使用加密模块进行加密,不能安装任何新的npm包。 - Suchetha B

0

解决方案比预期的简单。RijndaelManaged 代码使用 keylen 128 参考 AES-128 算法,并在 nodeJS 中使用 aes-128-cbc。

此外,由于 C# 代码使用 getBytes 获取密钥和 iv 值。因此,在 nodeJS 中必须同时使用 Buffer.from(secret) 来获取密钥和 iv 值。

最终解决方案如下:

   const crypto = require('crypto');
      algorithm = "aes-128-cbc",
      secret = '1234567890123456',
      keystring = new Buffer(secret),
      iv = new Buffer(secret),
      inputEncoding = 'utf8',
      outputEncoding = 'base64';
    function encrypt(text) {
      let cipher = crypto.createCipheriv(algorithm,keystring, iv);
      let encrypted = cipher.update(text, inputEncoding, outputEncoding)
      encrypted += cipher.final(outputEncoding);
      return encrypted;
    }
    
    function decrypt(encrypted) {
      let decipher = crypto.createDecipheriv(algorithm,keystring, iv)
      let dec = decipher.update(encrypted, outputEncoding, inputEncoding)
      dec += decipher.final(inputEncoding);
      return dec;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接