使用"crypto-js"编码,使用"crypto"(Node)解码



我在浏览器中使用高级加密标准(AES(用"cypto-js"加密字符串,需要在服务器上用节点"crypto"解密。

我可以单独使用"crypto-js"进行加密/解密,但当我尝试使用"crcrypto.createDecipher"使用"crupto"(节点(进行解密时,我会收到"错误解密"或"错误块大小"的错误消息,具体取决于我的尝试。

例如:只使用"crypto-js"-效果良好

crypto-js
const cypherParams = CryptoJS.AES.encrypt('my message', 'passphrase')
const decrypted = CryptoJS.AES.decrypt(cypherParams, 'passphrase')
console.log(decrypted.toString(CryptoJS.enc.Utf8)) // 'my message' - works!

例如:用"crypto js"编码用"crupto"解码-导致错误

[client]
const cypherParams = CryptoJS.AES.encrypt('my message', 'passphrase')
[server]
const decipher = crypto.createDecipher('aes-256-cbc', 'passphrase');
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8'); 
// results in 'bad decrypt' or 'block size' error in console

console.log(decrypted); // this never executes

我试过:

  • 将decrypt中的加密算法更改为192或其他(但"crypto-js"文档称默认值为"256"是使用密码短语
  • 客户端上的base64编码。也尝试了十六进制编码

例如:

const cypherParams = CryptoJS.AES.encrypt('my message', 'passphrase')
const base64Encoded = cipherParams.toString(CryptoJS.enc.Base64)
and
const cypherParams = CryptoJS.AES.encrypt('my message', 'passphrase')
const cypherParams.ciphertext = cipherParams.toString(CryptoJS.enc.Base64)
  • 我使用"crypto.createDecipher"而不是"cryptocreateDecipheriv",因为我在这个项目中一直使用Node v8.12.0

我想就这样了……我感谢任何帮助或提示!

如果密钥以字符串形式传递,则CryptoJS.AES.encrypt()使用OpenSSL密钥推导函数(KDF(EVP_BytesToKey()来推导32字节的密钥(和16字节的IV(,即AES-256确实用于加密(此处和此处(。在此过程中,将生成一个随机的8字节salt,以确保每次生成不同的密钥/IV对
NodeJS方法crypto.createCipher()使用相同的KDF,但不应用salt,因此总是生成相同的密钥/IV对。因此crypto.createDecipher()也不考虑盐
总之,这意味着用CryptoJS.AES.encrypt()加密时生成的密钥对与用crypto.createDecipher()解密时生成的钥匙对不同,解密失败。

据我所知,这两种方法都不可能控制是否使用盐,因此无法消除不相容性。

因此,一种解决方案是省略内置的KDF(无论如何,它都被认为是弱的,这反过来就是crypto.createCipher()/crypto.createDecipher()被弃用的原因(,而是使用可靠的KDF,例如PBKDF2,并使用从中派生的密钥/IV对。
在CryptoJS端,您必须将密钥和IV作为WordArray传递,在NodeJS端,必须使用create.createDecipheriv()
加密和解密之间的连接是在密钥推导过程中随机生成的盐。salt不是秘密,通常与密文连接,并以这种方式传递给接收方。

您提到您使用的版本是Node v8.12.0,因此无法应用crypto.createDecipheriv()。但是crypto.createDecipheriv()从v0.1.94开始就可用了,所以它应该在您的环境中可用。


客户端加密示例实现(CryptoJS(:

// Generate random salt
var salt16 = CryptoJS.lib.WordArray.random(16);                                     // Random 16 bytes salt
// Derive key and IV via PBKDF2
var keyIV = CryptoJS.PBKDF2("My Passphrase", salt16, {
keySize: (32 + 16) / 4,                                                           // 12 words a 4 bytes = 48 bytes
iterations: 1000,                                                                 // Choose a sufficiently high iteration count
hasher: CryptoJS.algo.SHA256                                                      // Default digest is SHA-1       
}); 
var key32 = CryptoJS.lib.WordArray.create(keyIV.words.slice(0, 32 / 4));            // 8 words a 4 bytes = 32 bytes 
var iv16 = CryptoJS.lib.WordArray.create(keyIV.words.slice(32 / 4, (32 + 16) / 4)); // 4 words a 4 bytes = 16 bytes 
// Encrypt
var message = 'The quick brown fox jumps over the lazy dog';
var cipherParams = CryptoJS.AES.encrypt(message, key32, {iv:iv16});
var ciphertext = cipherParams.ciphertext;
// Concatenate salt and ciphertext
var saltCiphertext = salt16.clone().concat(ciphertext);
var saltCiphertextB64 = saltCiphertext.toString(CryptoJS.enc.Base64);               // This is passed to the recipient    
// Outputs
console.log("Salt:n", salt16.toString(CryptoJS.enc.Base64).replace(/(.{56})/g,'$1n'));
console.log("Ciphertext:n", ciphertext.toString(CryptoJS.enc.Base64).replace(/(.{56})/g,'$1n'));
console.log("Salt | Ciphertext:n", saltCiphertextB64.replace(/(.{56})/g,'$1n'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

服务器端解密示例实现(NodeJS(:

var crypto = require('crypto');
// Separate salt and ciphertext
var saltCiphertextB64 = 'lhBp/LKhv8TxeJYnLDy/F2oaQYScOVFVLLZxd00HiRy9fYy97lX2ZjGJt+S4x+GF9X0AEjAS9k8tUDHKCz4srQ==';  // Received from client
var saltCiphertextBuf = Buffer.from(saltCiphertextB64, 'base64');
var saltBuf = saltCiphertextBuf.slice(0,16);
var ciphertextBuf = saltCiphertextBuf.slice(16);
// Derive key and IV via PBKDF2
var keyIVBuf = crypto.pbkdf2Sync("My Passphrase", saltBuf, 1000, 32 + 16, 'sha256');
var keyBuf = keyIVBuf.slice(0, 32); 
var ivBuf = keyIVBuf.slice(32, 32 + 16);
// Decrypt
var decipher = crypto.createDecipheriv("aes-256-cbc", keyBuf, ivBuf);
var plaintextBuf = Buffer.concat([decipher.update(ciphertextBuf), decipher.final()]);
// Outputs
console.log("Plaintext: ", plaintextBuf.toString('utf8')); // Plaintext:  The quick brown fox jumps over the lazy dog

最新更新