簡體   English   中英

用“crypto-js”編碼並用“crypto”(節點)解碼

[英]Encode with 'crypto-js' and decode with 'crypto' (Node)

我正在使用 'cypto-js' 在瀏覽器中使用高級加密標准 (AES) 加密字符串,並且需要在服務器上使用 Node 'crypto' 對其進行解密。

我可以單獨使用“crypto-js”加密/解密,但是當我嘗試使用“crypto.createDecipher”使用“crypto”(節點)解密時,我收到“解密錯誤”或“錯誤塊”的錯誤消息大小'取決於我的嘗試。

例如:只使用 '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”編碼用“crypto”解碼 - 導致錯誤

[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

我試過了:

  • 將解密中的加密算法更改為 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' 而不是 'crypto.createDecipheriv' 因為我在這個項目中使用 Node v8.12.0

我想就是這樣......我感謝任何幫助或提示!

如果密鑰作為字符串傳遞, CryptoJS.AES.encrypt()使用 OpenSSL 密鑰派生函數 (KDF) EVP_BytesToKey()派生 32 字節密鑰(和 16 字節 IV),即確實 AES-256 適用於加密(這里這里)。 在此過程中會生成一個隨機的 8 字節鹽,以確保每次都會產生不同的密鑰/IV 對。
crypto.createCipher()方法crypto.createCipher()使用相同的 KDF,但不應用鹽,因此始終生成相同的密鑰/IV 對。 因此crypto.createDecipher()也不考慮鹽。
總之,這意味着,與加密時生成的密鑰對CryptoJS.AES.encrypt()是從與解密時所產生的密鑰對不同crypto.createDecipher()和解密失敗。

據我所知,這兩種方法都沒有提供控制是否使用鹽的可能性,因此無法消除不兼容性。

因此,一種解決方案是省略內置 KDF(無論如何都認為它很弱,這反過來又是為什么不推薦使用crypto.createCipher() / crypto.createDecipher()原因)並使用可靠的 KDF,例如 PBKDF2 並使用從它派生的密鑰/IV 對。
在 CryptoJS 方面,您必須將 key 和 IV 作為WordArray ,在WordArray方面,您必須使用create.createDecipheriv()
加密和解密之間的聯系是在密鑰推導過程中隨機生成的鹽。 鹽不是秘密的,通常與密文連接並以這種方式傳遞給接收者。

您提到您使用的版本是 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,'$1\\n')); console.log("Ciphertext:\\n", ciphertext.toString(CryptoJS.enc.Base64).replace(/(.{56})/g,'$1\\n')); console.log("Salt | Ciphertext:\\n", saltCiphertextB64.replace(/(.{56})/g,'$1\\n'));
 <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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM