【问题标题】:Is there a way to decrypt a string which is encrypted in PBKDF2 format in javascript cryptoJS有没有办法解密在 javascript cryptoJS 中以 PBKDF2 格式加密的字符串
【发布时间】:2021-06-04 23:22:45
【问题描述】:

您好,我正在使用普通 html 开发密码管理器 Web 应用程序。我正在使用 cryptoJS 的客户端加密将密码存储在 firebase firestore 中,并且目前正在使用 aes。我还使用了 firebase 身份验证和密码短语aes加密是firebase提供的uid代码看起来像这样

  var user = firebase.auth().currentUser;
myPassword = user.uid+"QWERTYUIOPLKJHGASDFZXCVMNBqwertyuiopasdfghjklzxcvbnm1234567890!@#$)(*&^%-+=><,.:;{}[]"; 
encrypted = CryptoJS.AES.encrypt(document.getElementById("id").value, myPassword);

和 PBKDF2 加密看起来像这样

var salt = CryptoJS.lib.WordArray.random(128 / 8);var key512Bits1000Iterations = CryptoJS.PBKDF2("Secret Passphrase", salt, {keySize: 512 / 32,iterations: 1000});

谁能告诉我们如何解密上面代码后得到的结果↑

【问题讨论】:

  • 模糊的问题。 PBKDF2 不是加密算法,它是基于密码的密钥派生功能版本 2 (PBKDF2)。请注意,当您使用密码调用 CryptoJS.AES.encrypt 时,它会生成一个 256 位密钥。
  • 好的理解,但是使用PBKDF2方法的目的你能解释一下
  • 正如@kelalaska 评论的那样,PBKDF2 是一个密码密钥派生函数,它从(弱)密码中派生出(强)加密密钥。从用户获取密码(或密码)时,使用 PBKDF2 可提高加密的安全强度

标签: javascript encryption client-side cryptojs pbkdf2


【解决方案1】:

关于密码哈希的一点点

PBKDF2 是一个基于密码的密钥派生函数,类似于 bcrypt、scrypt 和 Argon2,其中 Argon2 是 2015 年密码哈希竞赛的获胜者(尽可能使用 Argon2id)。他们过去常常从密码中获取密钥。那么问题来了

为什么我们需要一个特殊的函数来从密码中获取密钥,而不是仅仅使用hash(password)

答案取决于历史的进步。首先,记住攻击总是会变得更好,它们永远不会变得更糟。 Philippe Oechslin 介绍的彩虹桌

实际上是 Merkle-Hellman 工作的延伸,并应用于实际案例。

  • 1980 - Hellman, M. “密码分析时间记忆权衡

彩虹桌

Rainbow 表仅用于反转任何函数,例如加密和散列函数。在谈论哈希函数的反向时,必须小心,在这种情况下,它只是为给定的哈希值y找到一个原像x,这样y=hash(x)。如今,人们可以在网络上找到许多预先构建的彩虹表。

彩虹桌的防御措施是什么?每个密码使用不同的盐。在这种情况下,彩虹表是无用的。它们需要按盐建造,这太昂贵了。因此,我们可以说使用盐会杀死 Rainbows 表。

海量 GPU/ASIC/FPGA 搜索

虽然人们也可以使用 CPU 进行大规模并行化以进行密码搜索,但使用 GPU、ASIC/FPGA(其余部分将仅使用 GPU 一词)更有效,因为 CPU 是通用处理单元,而不是专用处理单元.

攻击者可以同时使用多个 GPU 来搜索密码。一个很好的例子是 hashcat。攻击者可以并行使用多个实例来搜索密码。这只是一个并行的蛮力机器。

有什么对策?

  • 迭代次数:可以增加迭代次数。假设您使用 SHA 进行密码散列,然后 x-iteration 调用 SHA xSHA(SHA(...(SHA(x)...))。这会使你减慢x 倍,但是,它们也会减慢攻击者x 倍。

    PBKDF2 将迭代作为参数来控制这一点。必须注意迭代计数,您可能不希望您的用户等待登录。自定义调整此值以使用户等待不超过一秒钟。我们今天可以看到的 100K、250K、500K、1M 次迭代。考虑到即使您使用 100K 迭代,您也会将密码搜索速度减慢 100K 次。

  • Memory-Hard:在这种情况下,密码散列算法使用的是可调整的内存,不能与时间内存或类似技术收缩,因此它们也消除了大量搜索。 GPU 没有太多内存来提供所有线程,因此它们严重瘫痪。

  • 并行度:Argon2 也使用并行化参数。这也减少了攻击者在 CPU 上的并行运行。

回到你的编码

myPassword = user.uid+"QWERTYUIOPLKJHGASDFZXCVMNBqwertyuiopasdfghjklzxcvbnm1234567890!@#$)(*&^%-+=><,.:;{}[]"; 
encrypted = CryptoJS.AES.encrypt(document.getElementById("id").value, myPassword);

当您以这种方式使用 CryptoJS 加密时,它使用非标准化的 OpenSSL KDF 进行密钥派生 (EvpKDF),并使用 MD5 作为散列算法和 1 次迭代。

虽然 MD5 仍然可以防止前映像,但它对于密码散列的速度很快。因此你需要改变它。您可以使用 PBKDF2 加密as

function encrypt (msg, pass) {
  var salt = CryptoJS.lib.WordArray.random(128/8);
  
  var key = CryptoJS.PBKDF2(pass, salt, {
      keySize: keySize/32,
      iterations: iterations
    });

  var iv = CryptoJS.lib.WordArray.random(128/8);
  
  var encrypted = CryptoJS.AES.encrypt(msg, key, { 
    iv: iv, 
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC
    
  });
  
  // salt, iv will be hex 32 in length
  // append them to the ciphertext for use  in decryption
  var transitmessage = salt.toString()+ iv.toString() + encrypted.toString();
  var encodeB4 = CryptoJS.enc.Base64.stringify(transitmessage);
  return encodeB4;
}

function decrypt (transitmessage, pass) {
  var decoded = const decoded = CryptoJS.enc.Utf8.stringify(transitmessage);

  var salt = CryptoJS.enc.Hex.parse(decoded.substr(0, 32));
  var iv = CryptoJS.enc.Hex.parse(decoded.substr(32, 32))
  var encrypted = decoded.substring(64);
  
  var key = CryptoJS.PBKDF2(pass, salt, {
      keySize: keySize/32,
      iterations: iterations
    });

  var decrypted = CryptoJS.AES.decrypt(encrypted, key, { 
    iv: iv, 
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC
    
  })
  return decrypted;
}

【讨论】:

  • 非常感谢@kelalaka 解决了我的问题。是的,我一定会尝试您提供的代码,我还注意到,在示例中,您提供的加密字符串要长得多。我是由于我对密码学的概念不熟悉,所以对加密有点卡住了。再次感谢。
  • 这只是一个例子。加密时要小心,这只是一个例子。根据您的情况,必须正确选择操作模式。
最近更新 更多