【问题标题】:CryptoJS AES encryption with ECB mode produces different results with the same params使用 ECB 模式的 CryptoJS AES 加密使用相同的参数产生不同的结果
【发布时间】:2020-04-05 16:47:03
【问题描述】:

正如answer 中提到的,我可以使用 ECB 模式将转换后的值反转回纯文本,而不仅仅是将其与另一个散列值进行比较。

但是,使用下面的代码 sn-p:

const x = CryptoJS.AES.encrypt('abc', '123', { mode: CryptoJS.mode.ECB }).toString()
const y = CryptoJS.AES.encrypt('abc', '123', { mode: CryptoJS.mode.ECB }).toString()

console.log(x, y, x === y)
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

我明白了:

U2FsdGVkX19blKXDRXfdgXyviCrZtouB0cPcJPoR/cQ= U2FsdGVkX1+1AwWqKWntLVkh7DtiZxPDYCDNsjmc8LM= false

我做错了吗?有没有办法达到预期的效果?

【问题讨论】:

  • 读取the CryptoJS documentation. 加密的结果是一个对象。您只是将整个对象转换为字符串,这没有任何意义。
  • @Pointy 你是什么意思?我在库的readme中提到的对象上调用toString方法@
  • 您当然可以这样做,但对象详细信息包含您问题的答案。加密过程涉及随机数。

标签: javascript cryptojs


【解决方案1】:

首先:对于相同的明文和相同的密钥,在ECB模式下总是生成相同的密文!

如果将WordArray 用作第二个参数,则CryptoJS.AES.encrypt 使用密钥执行加密,生成的密文相同如预期(here ):

function encryptWithKey(plaintext, key){
    var encrypted = CryptoJS.AES.encrypt(plaintext, key, { mode: CryptoJS.mode.ECB });
    console.log("Ciphertext (Base64):\n" + encrypted.toString());        // Ciphertext
    var decrypted = CryptoJS.AES.decrypt(encrypted.toString(), key, { mode: CryptoJS.mode.ECB });
    console.log("Decrypted:\n" + decrypted.toString(CryptoJS.enc.Utf8)); // Plaintext
}

var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');
encryptWithKey('abc', key);
encryptWithKey('abc', key);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

但如果将字符串用作第二个参数,CryptoJS.AES.encrypt使用密码进行加密,并且生成的密文不同 (here)。尽管如此,解密当然会返回原始明文:

function encryptWithPassphrase(plaintext, passphrase){
    var encrypted = CryptoJS.AES.encrypt(plaintext, passphrase, { mode: CryptoJS.mode.ECB });
    console.log("Ciphertext (OpenSSL):\n" + encrypted.toString());       // Salt and actual ciphertext in OpenSSL format
    var decrypted = CryptoJS.AES.decrypt(encrypted.toString(), passphrase, { mode: CryptoJS.mode.ECB });
    console.log("Decrypted:\n" + decrypted.toString(CryptoJS.enc.Utf8)); // Plaintext
}

encryptWithPassphrase('abc', '123'); 
encryptWithPassphrase('abc', '123');
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

解释:
在使用密码加密期间,会生成一个 随机 8 字节的盐,从该盐与密码一起生成实际的密钥(32 字节,AES-256)。
盐旨在使使用彩虹表不可行。 由于每次随机生成盐,生成的密钥不同,因此密文也不同。
CryptoJS.AES.encrypt 返回一个CipherParams 对象,该对象封装了盐和实际密文等相关参数。
toString() 将此对象转换为 OpenSSL 格式,该格式由Salted__ 的 ASCII 编码、后跟 8 个字节的盐、然后是实际的密文组成,所有这些都是 Base64 编码的。因此,所有密文都以U2FsdGVkX1 开头。

function encryptWithPassphraseParams(plaintext, passphrase){
    var encrypted = CryptoJS.AES.encrypt(plaintext, passphrase, { mode: CryptoJS.mode.ECB });
    console.log("Salt (hex):\n" + encrypted.salt);                 // Salt (hex)
    console.log("Key (hex):\n" + encrypted.key);                   // Key (hex)
    console.log("Ciphertext (hex):\n" + encrypted.ciphertext);     // Actual ciphertext (hex)
    console.log("Ciphertext (OpenSSL):\n" + encrypted.toString()); // Salt and actual ciphertext, Base64 encoded, in OpenSSL format
    console.log("\n");
}

encryptWithPassphraseParams('abc', '123'); 
encryptWithPassphraseParams('abc', '123');
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

详情:
CryptoJS 使用带有摘要 MD5 的 OpenSSL 功能EVB_BytesToKey,在派生密钥时迭代计数为 1,这不是很安全。更安全的是使用可靠的 KDF,例如 PBKDF2 以及随后使用生成的密钥进行加密。
除了安全性之外,应该注意EVB_BytesToKey 没有实现标准,因此必须首先实现此功能(或从 Internet 复制)在图书馆不可用。

注意: ECB 是一种不安全的模式,不应该使用 (here),更好的是像 GCM 这样的经过身份验证的加密。有关 CryptoJS 的更多详细信息,请参阅其文档 (here)。

【讨论】:

  • 非常简洁的解释!现在我明白了很多。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-05
  • 2017-09-08
  • 2016-12-18
  • 2011-10-04
  • 2016-12-17
  • 2018-08-17
相关资源
最近更新 更多