如answer中所述,我可以使用ECB模式将转换后的值反向转换为纯文本,而不仅仅是将其与另一个哈希值进行比较。
但是,使用下面的代码段:
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
我做错什么了吗?有没有办法达到预期的结果?
首先:对于相同的明文和相同的密钥,在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): " + encrypted.toString()); // Ciphertext
var decrypted = CryptoJS.AES.decrypt(encrypted.toString(), key, { mode: CryptoJS.mode.ECB });
console.log("Decrypted: " + 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>
但是如果将字符串用作第二个参数,则(here)。不过,解密当然会返回原始的纯文本:CryptoJS.AES.encrypt
执行使用密码加密,并且所得密文为不同
function encryptWithPassphrase(plaintext, passphrase){
var encrypted = CryptoJS.AES.encrypt(plaintext, passphrase, { mode: CryptoJS.mode.ECB });
console.log("Ciphertext (OpenSSL): " + encrypted.toString()); // Salt and actual ciphertext in OpenSSL format
var decrypted = CryptoJS.AES.decrypt(encrypted.toString(), passphrase, { mode: CryptoJS.mode.ECB });
console.log("Decrypted: " + 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>
当导出密钥时,CryptoJS使用带有摘要MD5和1的迭代计数的OpenSSL功能说明:在使用密码短语进行加密的过程中,会生成random 8字节的盐,从中与密码短语一起生成实际的密钥(32字节,AES-256)。该盐旨在使彩虹表的使用不可行。 由于每次都会随机生成盐,因此生成的密钥不同,因此密文也不同。
CryptoJS.AES.encrypt
返回一个CipherParams
对象,该对象封装了盐和实际密文之类的相关参数。CipherParams
转换该对象格式转换为OpenSSL格式,该格式由toString()
的ASCII编码组成,后跟8个字节的盐,然后是实际的密文,均由Base64编码。因此,所有密文均以Salted__
开头。U2FsdGVkX1
function encryptWithPassphraseParams(plaintext, passphrase){ var encrypted = CryptoJS.AES.encrypt(plaintext, passphrase, { mode: CryptoJS.mode.ECB }); console.log("Salt (hex): " + encrypted.salt); // Salt (hex) console.log("Key (hex): " + encrypted.key); // Key (hex) console.log("Ciphertext (hex): " + encrypted.ciphertext); // Actual ciphertext (hex) console.log("Ciphertext (OpenSSL): " + 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>
,这不是很安全。使用可靠的KDF(例如PBKDF2)以及随后使用生成的密钥进行的加密,会更加安全。除安全性外,应注意EVB_BytesToKey
没有实现标准,因此必须首先在不可用的库中实现(或从Internet复制)此功能。注意:
ECB是一种不安全的模式,不应使用(EVB_BytesToKey
),最好是经过身份验证的加密,例如GCM。有关CryptoJS的更多详细信息,请参见其文档(EVB_BytesToKey
)。