【问题标题】:EVP_DecryptFinal_ex:bad decrypt when using Node.jsEVP_DecryptFinal_ex:使用 Node.js 时解密错误
【发布时间】:2016-10-26 03:01:53
【问题描述】:

使用以下节点js:

var crypto = require('crypto');
var encrypt = function (input, password, callback) {
    var m = crypto.createHash('md5');
    m.update(password);
    var key = m.digest('hex');

    m = crypto.createHash('md5');
    m.update(password + key);
    var iv = m.digest('hex');
    console.log(iv);

    var data = new Buffer(input, 'utf8').toString('binary');

    var cipher = crypto.createCipheriv('aes-256-cbc', key, iv.slice(0,16));
    var encrypted = cipher.update(data, 'binary') + cipher.final('binary');
    var encoded = new Buffer(encrypted, 'binary').toString('base64');
    callback(encoded);
};

var decrypt = function (input, password, callback) {
    // Convert urlsafe base64 to normal base64
    input = input.replace(/\-/g, '+').replace(/_/g, '/');
    // Convert from base64 to binary string
    var edata = new Buffer(input, 'base64').toString('binary');

    // Create key from password
    var m = crypto.createHash('md5');
    m.update(password);
    var key = m.digest('hex');

    // Create iv from password and key
    m = crypto.createHash('md5');
    m.update(password + key);
    var iv = m.digest('hex');

    // Decipher encrypted data
    var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv.slice(0,16));
    var decrypted = decipher.update(edata, 'binary') + decipher.final('binary');
    var plaintext = new Buffer(decrypted, 'binary').toString('utf8');

    callback(plaintext);
};

为了执行,我运行了这个:

encrypt("uWeShxRrCKyK4pcs", "secret", function (encoded) {
    console.log(encoded);
    decrypt(encoded, "secret", function (output) {
        console.log(output);
    });
});

加密似乎工作正常,但是当我尝试解密时,我收到以下错误:

错误:错误:06065064:数字信封 例程:EVP_DecryptFinal_ex:错误解密 在错误(本机) 在 Decipheriv.Cipher.final (crypto.js:202:26)

我对密码学很陌生,所以我真的不知道为什么我会收到这个错误。我现在只需要修复它。

【问题讨论】:

  • 1.不应该使用 MD5,它不安全,密码的简单散列也不够安全。 2. 使用随机盐在 HMAC 上迭代大约 100 毫秒(盐需要与哈希一起保存)。使用 password_hash、PBKDF2、Bcrypt 等函数或类似函数。关键是让攻击者花费大量时间通过蛮力寻找密码。请参阅 OWASP(开放式 Web 应用程序安全项目)Password Storage Cheat Sheet。
  • 谢谢,但我不知道如何更改我的代码来做到这一点。我会尝试看看 bcrypt。
  • 调试时间。十六进制转储加密和解密的密钥和 iv,并验证它们是否正确以及长度是否正确。 AES 密钥应该是 32 字节,iv 应该是 16 字节。十六进制转储加密后立即加密和解密前再次加密,验证它们是否相同。
  • 如果这是一个有很多用户的严肃应用程序,您需要让领域专家至少审查代码以及如何使用加密。即使对于经验丰富的开发人员来说,加密安全性也很难做到正确。
  • 一切似乎都是正确的。我注意到的是,如果我使用比 16 更短的密码,那么一切正常。它似乎与这个问题有关:stackoverflow.com/questions/33586060/… 但是我的代码已经在答案中建议了更改。

标签: node.js encryption openssl cryptography node-crypto


【解决方案1】:

您混淆了两种不同的编码。见

cipher.update(data[, input_encoding][, output_encoding])

和

cipher.final([output_encoding])

现在看看

var encrypted = cipher.update(data, 'binary') + cipher.final('binary');

但应该是的

var encrypted = cipher.update(data, 'binary', 'binary') + cipher.final('binary');

问题在于cipher.update(data, 'binary') 输出的缓冲区会自动字符串化为十六进制编码字符串,而不是“二进制”字符串。


无论如何,这段代码有很多错误,您应该重新开始,并简单地使用一个高度固执己见的现有库。

  • 您必须有一个随机IV,它被预先添加到密文中以达到语义安全。

  • 密码熵低,不能用作密钥。单个 MD5 调用不会改变这一事实。从密码导出密钥应该很慢,因此请使用具有高迭代次数/成本因素的已知方案,例如 PBKDF2、bcrypt、scrypt 或 Argon2(提高安全性)。别忘了盐。

  • 在先加密后 MAC 方案中使用消息验证码(例如 HMAC-SHA256)验证您的密文。否则,攻击者可能会操纵密文,您甚至无法检测到更改。使用 padding oracle 攻击丢失数据的第一步。

【讨论】:

    【解决方案2】:

    由于这个问题是第一个出现在 Google 上的问题,这里有另一个解决方案。

    lifesaver bad decrypt

    如果链接断开,就我而言,我必须更新解密功能以添加

    function decrypt(text) {
        let iv = Buffer.from((text.split(':')[1]).split('=')[0], 'hex')//will return iv;
        let enKey = Buffer.from(text.split('=')[1], 'hex')//will return key;
        let encryptedText = Buffer.from(text.split(':')[0], 'hex');//returns encrypted Data
        let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(enKey), iv);
    // Added this line here
        decipher.setAutoPadding(false);
        let decrypted = decipher.update(encryptedText);
        decrypted = Buffer.concat([decrypted, decipher.final()]);
        return decrypted.toString();
        //returns decryptedData
    }
    

    【讨论】:

      【解决方案3】:

      我发现了这个原因,是使用不同的 key 或 iv's 进行加密和解密。我们必须使用用于加密和解密内容的相同密钥 & iv。唯一的解决方法是将 iv & key 保存在加密数据期间使用的数组中,或在分隔符的帮助下将 iv & key 与加密数据连接

      示例一:

      function encrypt(text) {
          let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
          let encrypted = cipher.update(text);
          encrypted = Buffer.concat([encrypted, cipher.final()]);
          return encrypted.toString('hex') + ':' + iv.toString('hex') + '=' + 
          key.toString('hex');
          //returns encryptedData:iv=key
      }
      
      function decrypt(text) {
          let iv = Buffer.from((text.split(':')[1]).split('=')[0], 'hex')//will return iv;
          let enKey = Buffer.from(text.split('=')[1], 'hex')//will return key;
          let encryptedText = Buffer.from(text.split(':')[0], 'hex');//returns encrypted Data
          let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(enKey), iv);
          let decrypted = decipher.update(encryptedText);
          decrypted = Buffer.concat([decrypted, decipher.final()]);
          return decrypted.toString();
          //returns decryptedData
      }
      

      示例二:

      function encrypt(text) {
          let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
          let encrypted = cipher.update(text);
          encrypted = Buffer.concat([encrypted, cipher.final()]);
          return { 
              "encryptedData": encrypted.toString('hex'),
              "iv" : iv.toString('hex'),
              "key" : key.toString('hex');
          //returns an Array of key, iv & encryptedData
        }
      }
      
      function decrypt(text) {
          let iv = Buffer.from((text.iv, 'hex')//will return iv;
          let enKey = Buffer.from(text.key, 'hex')//will return key;
          let encryptedText = Buffer.from(text.encryptedData, 'hex');//returns encrypted Data
          let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(enKey), iv);
          let decrypted = decipher.update(encryptedText);
          decrypted = Buffer.concat([decrypted, decipher.final()]);
          return decrypted.toString();
          //returns decryptedData
      }
      

      【讨论】:

      • 等等什么?您将密钥与密文一起存储。这不提供任何安全性,而只是简单的混淆,因为进行解密的密钥就在密文中。 IV 不应该是秘密的,可以与密文一起存储。另一方面,密钥是唯一需要保密的东西。
      猜你喜欢
      • 2020-04-30
      • 1970-01-01
      • 2016-03-22
      • 1970-01-01
      • 2019-04-22
      • 2023-04-05
      • 2016-08-09
      • 2021-10-19
      • 2014-05-02
      相关资源
      最近更新 更多