如何生成输入相同输出一致、长度与输入匹配的确定性哈希令牌
问题根源分析
- 密文过长:你输入的原始令牌是64位十六进制字符串(对应32字节二进制数据),直接将字符串作为明文传入加密函数时,处理的是64字节的UTF-8编码数据,加密后转十六进制输出自然长度翻倍。部分加密实现还会把随机IV拼接到密文前,进一步增加长度。
- 重启后结果变化:你每次加密都生成随机IV作为参数,AES-CTR模式下IV变化会导致相同明文+相同密钥的加密结果完全不同,容器重启后重新生成IV自然结果无法匹配历史存储值。
可行实现方案
根据你的需求(等长输出、相同输入固定输出、低安全级别要求),推荐两种方案,按需选择:
方案1:HMAC-SHA256(不可逆,适合仅做重复校验场景,优先推荐)
该方案输出刚好是64位十六进制字符串,和你的输入格式、长度完全一致,只要密钥固定,相同输入输出永远相同,性能更高实现更简单。
代码示例:
const { createHmac } = require('crypto'); // 密钥需固定存入环境变量,不要硬编码,容器重启也不会变更 const HMAC_KEY = process.env.TOKEN_HMAC_KEY || '自定义固定密钥字符串'; function generateHashToken(rawToken) { return createHmac('sha256', HMAC_KEY) .update(rawToken) .digest('hex') .toUpperCase(); // 如需要和输入一致的全大写格式可保留该行 } // 测试 const rawToken = '9ECB531EC6AE02CB0274336690E76A0A39511429859537BE242147B65B1824DD'; console.log(generateHashToken(rawToken));
优势说明:
- 输出天然为64位十六进制,和输入长度格式完全匹配
- 密钥固定的前提下,相同输入100%输出相同结果,不受容器重启影响
- 带密钥的哈希算法无法反向碰撞出原始令牌,满足非敏感数据保护需求
- 实现简单,性能远高于对称加密方案
方案2:固定IV的AES-256-CTR(可逆,适合需要还原原始令牌的场景)
如果后续需要从加密结果还原原始令牌,可使用该方案,需注意编码处理保证长度一致。
代码示例:
const { createCipheriv, createDecipheriv, scryptSync } = require('crypto'); // 密钥、IV、盐都固定存入环境变量,不要动态生成 const AES_KEY = scryptSync(process.env.TOKEN_AES_PASSWORD || '自定义固定密码', '固定盐值', 32); // IV固定为16字节,这里示例用hex字符串转换,可自行替换为你的固定值 const FIXED_IV = Buffer.from(process.env.TOKEN_AES_IV || '0123456789abcdef0123456789abcdef', 'hex'); function encryptToken(rawToken) { // 先将输入的hex字符串转为32字节Buffer,不要直接传入UTF-8字符串 const rawBuffer = Buffer.from(rawToken, 'hex'); const cipher = createCipheriv('aes-256-ctr', AES_KEY, FIXED_IV); const encryptedBuffer = Buffer.concat([cipher.update(rawBuffer), cipher.final()]); // 输出转为hex字符串,刚好64位,和输入长度一致 return encryptedBuffer.toString('hex').toUpperCase(); } function decryptToken(encryptedToken) { const encryptedBuffer = Buffer.from(encryptedToken, 'hex'); const decipher = createDecipheriv('aes-256-ctr', AES_KEY, FIXED_IV); const decryptedBuffer = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()]); return decryptedBuffer.toString('hex').toUpperCase(); }
注意事项:
- 绝对不能使用动态生成的随机IV,否则相同输入的加密结果会变化
- 必须将原始令牌先按hex编码转为Buffer,不能直接传入字符串,否则输出长度会翻倍
注意事项
所有涉及的固定密钥、盐、IV都要存入服务的环境变量配置中,不要硬编码到代码里,既保证配置不会随容器重启变更,也能提升安全性。
内容的提问来源于stack exchange,提问作者Royal
相关产品推荐
相关产品推荐

