【问题标题】:AES encryption in Node JS and C# gives different resultsNode JS 和 C# 中的 AES 加密给出不同的结果
【发布时间】:2020-10-08 08:10:08
【问题描述】:

我有一个用例,必须使用 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 中寻找等效的操作。

【问题讨论】:

  • 为小提琴点赞,干得好,祝你好运
  • 您是否有理由忽略 algorithm 并将其硬编码为 aes-128-cbc
  • @John - 没有理由,我试图查看用户过去关于相同问题的其他建议,并尝试了 aes-128。
  • 我只是想知道这是否是您的问题,因为您的 C# 代码使用 AES-256。
  • @Topaco - 我在这里有点困惑。 C# 使用 256 的块大小。如果必须将其更改为 128 以使其成为 aes-256 算法,那在我的情况下是不可行的。 C# 代码早已投入生产,无法修改。只有 Node JS 实现与 C# 同步。

标签: javascript c# node.js aes cryptojs


【解决方案1】:

解决方案比预期的要简单。带有 keylen 128 的 RijndaelManaged 代码引用了 AES-128 算法并在 nodeJS 中使用 aes-128-cbc。

此外,由于 C# 代码使用 getBytes 作为键和 iv 值。 Buffer.from(secret) 必须用于 nodeJS 中的键和 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;
    }

【讨论】:

    【解决方案2】:

    整个问题可能与编码有关。 .Net 加密类默认使用 Unicode 或 utf16-le。虽然您已将 base64 指定为输出编码。 将其更改为 utf16le 即

    outputEncoding = 'utf16le';
    

    在这里查看Node Js documentation

    【讨论】:

      【解决方案3】:

      试试这个:

      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 包。
      【解决方案4】:

      TLDR;

      您使用不同的 IV 和算法(AES-128 与 AES-256),因此您将获得不同的结果...

      如果您想获得相同的结果,您将需要使用相同的 IV 以及相同的密钥和算法。 这将是一种反模式(即不要这样做)!请查看 John 关于您如何忽略代码中的算法变量的评论,快速看看这一点,不同的 IV 会导致您得到不同的结果。

      更长的答案;

      1) 您实际上希望它使用相同的密钥加密的相同消息(纯文本)并不总是产生相同的加密结果(密文)。否则,任何窃听方将始终知道何时再次发送了重复消息。

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

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

      4) IV 应该是随机的,并且不是从密钥确定性派生的,否则每次使用相同的密钥都将具有相同的 IV,因此每次加密相同的纯文本时都会产生相同的密文。 (让您容易受到窃听者观察收到的给定消息的结果并开始确定消息的含义)。

      查看此问题的答案AES Encryption - Key versus IV 以及此维基百科条目http://en.wikipedia.org/wiki/Initialization_vector 了解更多信息。

      【讨论】:

      • 在这种情况下,键和 IV 值将相同,因为我正在尝试匹配使用相同字符串生成这些的 C# 代码。
      • 当然,在您对代码进行故障排除/调试时,可以将它们设置为相同。但是,当您开始在生产中使用它时,请注意每次加密时都应使用随机 IV。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-10
      相关资源
      最近更新 更多