为什么我的端到端加密解密后得到乱码?

ლ薪羽 阅读 26

我在用JavaScript实现聊天应用的端到端加密时遇到问题,用crypto库的AES加密发送的消息,另一端解密后总是乱码。尝试过确保密钥一致和正确设置填充模式,但还是不行。代码大概是这样的:


const crypto = require('crypto');
function encrypt(text, key) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
  return iv + cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
}

解密时发现拼接方式有问题,但不确定具体哪里出错。测试时本地加密解密没问题,但通过网络传输后就乱了。是不是密钥长度或编码方式哪里没对齐?

我来解答 赞 4 收藏
二维码
手机扫码查看
2 条解答
Mc.书圻
Mc.书圻 Lv1
你这个问题主要是加密和解密的数据格式没对齐,尤其是拼接的处理方式有问题。JS里面字符串和二进制数据混用的时候特别容易踩坑。

先说问题根源:你在加密时用了 iv + cipher.update(...) + cipher.final(...) 的方式直接拼接了初始化向量(IV)和加密后的数据,但这里的 iv 是二进制数据,而后面加密结果是十六进制字符串,两者类型不一致。通过网络传输后,这种不一致会导致解密端解析失败。

解决方法是统一编码格式,比如把 IV 和加密数据都转成十六进制或 Base64 再拼接。下面是修正后的代码:

const crypto = require('crypto');

function encrypt(text, key) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
const encrypted = cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
// 把 IV 转成 hex 后再拼接
return iv.toString('hex') + encrypted;
}

function decrypt(encryptedData, key) {
// 提取前 32 个字符作为 IV(因为 IV 是 16 字节,转 hex 后长度是 32)
const iv = Buffer.from(encryptedData.slice(0, 32), 'hex');
const data = encryptedData.slice(32); // 剩下的部分是加密数据
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
return decipher.update(data, 'hex', 'utf8') + decipher.final('utf8');
}


注意几个关键点:
1. 加密时,把 IV 转成十六进制字符串后再拼接。
2. 解密时,先提取出 IV 部分,再处理剩下的加密数据。
3. 确保密钥长度是 32 字节(AES-256 要求),如果密钥是字符串,记得用类似 crypto.scryptSync 或其他方法生成固定长度的密钥。

最后提醒一下,调试加密问题时尽量在本地模拟完整流程,确保加解密逻辑没问题后再放到网络环境中测试。乱码通常是数据格式不一致导致的,多检查输入输出的编码和长度就能找到问题。
点赞 1
2026-02-15 04:06
极客雨辰
你的问题出在加密数据的拼接方式上,iv + cipher.update() + cipher.final() 这种字符串拼接会破坏二进制数据。IV 是 Buffer,直接用 + 拼接会隐式转成字符串,导致解密时拿不到原始 IV。

正确做法是把 IV 和密文都转成十六进制或 Base64,然后组合成一个可传输的字符串。比如这样:

function encrypt(text, key) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
// 返回 iv 和密文的 hex 拼接
return iv.toString('hex') + ':' + encrypted;
}

function decrypt(encryptedData, key) {
const [ivHex, encryptedHex] = encryptedData.split(':');
const iv = Buffer.from(ivHex, 'hex');
const encryptedText = Buffer.from(encryptedHex, 'hex');
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}


注意:key 要确保是 32 字节的 Buffer,如果不是得用 crypto.scrypthash 处理一下。还有就是前后端传的时候别让中间件乱改编码,最好整个加密串走 Base64 而不是拼接 hex。

这种基础加密逻辑其实插件可以省不少事,比如 crypto-js 就封装好了,但你自己写也行,关键是别乱拼 Buffer 和字符串。
点赞 4
2026-02-12 15:06