这里记录一下sm4加密过程中,关于秘钥这一块的踩坑经历。

看到这里你嘴上可能已经在骂骂咧咧,秘钥不就是一个字符串,管他啥加密原理,直接用不就行了,还能有啥整不明白的。直到我加密测试之后,才发现事情并不简单。

这是部分前端源代码:

if(key == undefined || key == null || key.length%16 != 0){
    console.log("sm4 key is error!");
    return null;

控制台直接打印 sm4 key is error!。这个工程是基于原来老项目开发的,所以跑回去看了一下老代码,这个密钥串长度是32,通过计算后key.length为16,是OK的。而这次后台给我的是一个长度16的字符串,key.length为8,所以直接进这个错误if了。

const oldKey = '464432503645684239534362734c5938'
const newKey = 'FD2P6EhB9SCbsLY8'

后台给的newKey串长度为16,我有确认应该没问题。因为sm4加密的密钥长度是128位,1个字母或者数字占1个字节,1个字节8位,16*8=128位,没毛病。而前端工程里,必须传一个length为32的串,这是玩哪样,内心一句羊羊羊(自行补脑)在奔腾。

然后我用老的串加密测试,结果两边加密出来的东西不一致,目前唯一可行的就是想办法把这个长度16的串转成长度32的串。

解决问题的思路

既然秘钥是16进制的串,那就先把字符串转成UTF-16格式的数字,再把数字转成16进制,也许就成功了呢!

预期 'F' === '46''D' === '44''2' === '32''P' === '50'...我们使用2个关键的API,charCodeAttoString 方法。

const oldKey = '464432503645684239534362734c5938'
const newKey = 'FD2P6EhB9SCbsLY8'
const first = newKey[0] // 'F'
const toUTF16 = first.charCodeAt() // utf-16 70
const toHex = toUTF16.toString(16) // hex '46'
toHex === '46' // true
// 再试一个
const first = newKey[1] // 'D'
const toUTF16 = first.charCodeAt() // utf-16 68
const toHex = toUTF16.toString(16) // hex '44'
toHex === '44' // true

测试验证通过,思路完全正确。