如何用Node.js Crypto的AES-256-CBC加密超块大小文本?
问题分析与解决方案
原代码错误原因
你的代码出现乱码的核心问题是Buffer与字符串直接拼接导致的编码错误:
cipher.update(message)返回Buffer类型,直接和cipher.final("hex")的字符串相加时,Buffer会被强制转为UTF-8字符串,但加密后的二进制数据并非合法UTF-8序列,因此出现乱码。- 这个问题和文本长度无关,只是短文本恰好没触发明显的编码错误而已。
修复方案(无需分片)
如果不需要强制分片,只需修正加密时的Buffer处理逻辑即可:
const AES_ALGORITHM = 'aes-256-cbc'; // 原代码缺失该常量,需补充 var { randomBytes, createCipheriv, createDecipheriv, } = require("crypto"); var config = require("../../config"); var { hkdfKeygen } = require("./hashing"); function encryptAes(message, key) { try { const encryption_key_buffer = hkdfKeygen( key, config.hkdfSalt, config.hkdfAdditionalInfo, 32 ); const initialization_vector_buffer = randomBytes(16); const cipher = createCipheriv( AES_ALGORITHM, encryption_key_buffer, initialization_vector_buffer ); // 用Buffer.concat合并update和final的结果,再统一转hex const encryptedBuffer = Buffer.concat([cipher.update(message, 'utf8'), cipher.final()]); const encrypted = encryptedBuffer.toString('hex'); return { encrypted, iv: initialization_vector_buffer.toString("hex"), }; } catch (err) { throw new Error("Error encrypting in AES -> " + err.message); } } function decryptAes(ciphertext, key, iv) { try { const encryption_key_buffer = hkdfKeygen( key, config.hkdfSalt, config.hkdfAdditionalInfo, 32 ); const initialization_vector_buffer = Buffer.from(iv, "hex"); const decipher = createDecipheriv( AES_ALGORITHM, encryption_key_buffer, initialization_vector_buffer ); // 解密时统一处理,避免编码问题 const decryptedBuffer = Buffer.concat([decipher.update(ciphertext, 'hex'), decipher.final()]); return decryptedBuffer.toString('utf8'); } catch (err) { throw new Error("Error decrypting in AES -> " + err.message); } }
分片加密实现(按需求)
如果确实需要分片加密(比如处理超大文件或特定业务场景),需注意:
- AES-CBC的每个分片需要使用链式IV(前一个分片的密文最后一块作为下一个分片的IV),否则会失去CBC模式的安全性。
- 分片大小按你要求设为
2*BLOCK_SIZE=32字节(BLOCK_SIZE=16字节)。
以下是分片加密解密的完整实现:
const AES_ALGORITHM = 'aes-256-cbc'; const BLOCK_SIZE = 16; const CHUNK_SIZE = 2 * BLOCK_SIZE; // 32字节 var { randomBytes, createCipheriv, createDecipheriv, } = require("crypto"); var config = require("../../config"); var { hkdfKeygen } = require("./hashing"); // 分片加密 function encryptAesChunked(message, key) { try { const encryption_key_buffer = hkdfKeygen( key, config.hkdfSalt, config.hkdfAdditionalInfo, 32 ); const originalMessageBuffer = Buffer.from(message, 'utf8'); const chunks = []; let currentIv = randomBytes(BLOCK_SIZE); // 初始IV const ivs = [currentIv.toString('hex')]; // 保存所有IV(初始IV+链式IV) // 切割文本为分片 for (let i = 0; i < originalMessageBuffer.length; i += CHUNK_SIZE) { const chunk = originalMessageBuffer.slice(i, i + CHUNK_SIZE); const cipher = createCipheriv(AES_ALGORITHM, encryption_key_buffer, currentIv); const encryptedChunk = Buffer.concat([cipher.update(chunk), cipher.final()]); chunks.push(encryptedChunk.toString('hex')); // 更新IV为当前密文的最后16字节(链式IV) currentIv = encryptedChunk.slice(-BLOCK_SIZE); ivs.push(currentIv.toString('hex')); } return { encryptedChunks: chunks, ivs: ivs, // 需要保存所有IV用于解密 }; } catch (err) { throw new Error("Error encrypting in AES (chunked) -> " + err.message); } } // 分片解密 function decryptAesChunked(encryptedChunks, key, ivs) { try { const encryption_key_buffer = hkdfKeygen( key, config.hkdfSalt, config.hkdfAdditionalInfo, 32 ); const decryptedChunks = []; let currentIv = Buffer.from(ivs[0], 'hex'); for (let i = 0; i < encryptedChunks.length; i++) { const encryptedChunkBuffer = Buffer.from(encryptedChunks[i], 'hex'); const decipher = createDecipheriv(AES_ALGORITHM, encryption_key_buffer, currentIv); const decryptedChunk = Buffer.concat([decipher.update(encryptedChunkBuffer), decipher.final()]); decryptedChunks.push(decryptedChunk); // 更新IV为当前密文的最后16字节 currentIv = encryptedChunkBuffer.slice(-BLOCK_SIZE); } return Buffer.concat(decryptedChunks).toString('utf8'); } catch (err) { throw new Error("Error decrypting in AES (chunked) -> " + err.message); } } // 使用示例 const testMessage = " This is is a kind of text that my implementaion will spit out partial gibberish for "; const testKey = "your-secret-key"; // 替换为实际密钥 // 分片加密 const encryptedResult = encryptAesChunked(testMessage, testKey); console.log("Encrypted chunks:", encryptedResult.encryptedChunks); // 分片解密 const decryptedMessage = decryptAesChunked(encryptedResult.encryptedChunks, testKey, encryptedResult.ivs); console.log("Decrypted message:", decryptedMessage);
注意事项
- 原代码中缺失
AES_ALGORITHM常量,必须补充为'aes-256-cbc'才能正常运行。 - 分片加密时必须保存所有IV(初始IV+每个分片的链式IV),否则无法正确解密。
- 无论是否分片,都要确保密钥的安全性,避免硬编码密钥。
内容的提问来源于stack exchange,提问作者Timileyin Pelumi
相关产品推荐
相关产品推荐

