背景
意外接触到SM2算法,因为对接别人API,需要使用SM2加密,对方再解密,对方使用的示例代码是用Hutool写的,但是Node没有一个完整的示例去做转换
依赖
问题
1. JAVA与Node生成的Key不一致
JAVA生成publicKey和privateKey
KeyPair pair = SecureUtil.generateKeyPair("SM2");
byte[] privateKey = pair.getPrivate().getEncoded();
byte[] publicKey = pair.getPublic().getEncoded();
System.out.printf("privateKey:%s \n",HexUtil.encodeHexStr(privateKey));
// privateKey:308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420730895115e28d7cdbc5be77e0a2c39f690e04217b7218f7c36f27e293b7d1e51a00a06082a811ccf5501822da1440342000498024e7a2ad38f79223394aab9a30fd30be81c6c5efd307d520ff5f53d9d4bee2f7e62c843ae2c0ff448dc6e56297fda6154d5110ea246e4b692c2d3bda96949
System.out.printf("publicKey:%s \n",HexUtil.encodeHexStr(publicKey));
// publicKey: 3059301306072a8648ce3d020106082a811ccf5501822d0342000498024e7a2ad38f79223394aab9a30fd30be81c6c5efd307d520ff5f53d9d4bee2f7e62c843ae2c0ff448dc6e56297fda6154d5110ea246e4b692c2d3bda96949
Node生成publicKey和privateKey
const { sm2 } = require('sm-crypto')
const keypair = sm2.generateKeyPairHex();
console.log(keypair.privateKey)
// 41b5659af1ecfbde9c82219c3a2abf573fcc3a7f86e58d1cf6ab7a3c0adda152
console.log(keypair.publicKey)
// 0415d214fbac17ef9bf164b6d64c1340b9d4d4feb99f3e616c73a8069d078ca0304a3a3f5ea8e7f8e8faf9dd76ad3d284c57cb9228ab9b4d0f53cacdec613eb19c
这里可以发现两个包产生的key长度不一致。从sm-crypto了解到有两种加密模式cipherMode:C1C3C2和C1C2C3
- C1:是生成随机数的计算出的椭圆曲线点
- C2:是密文数据
- C3是SM3的摘要值
问题解决
我翻了hutool项目里的一些PR和issues,给了我灵感
- github.com/dromara/hut…
- 我知道了生成的key里包含填充的一些hash,并不是全部都有用,并且长度是固定长度
- github.com/dromara/hut…
- github.com/dromara/hut…
- 这个PR里的这段UT让我发现,JAVA key里包含固定字段
06082A811CCF5501822D,这个时候我猜测可以从JAVA里生成KEY截取固定字符串让Node使用,通过对比随机生成JAVA的几次key,最终截取结果如下:
- 这个PR里的这段UT让我发现,JAVA key里包含固定字段
privateKey:
完整:308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420730895115e28d7cdbc5be77e0a2c39f690e04217b7218f7c36f27e293b7d1e51a00a06082a811ccf5501822da1440342000498024e7a2ad38f79223394aab9a30fd30be81c6c5efd307d520ff5f53d9d4bee2f7e62c843ae2c0ff448dc6e56297fda6154d5110ea246e4b692c2d3bda96949
拆解:
308193020100301306072a8648ce3d0201
06082a811ccf5501822d
047930770201010420
730895115e28d7cdbc5be77e0a2c39f690e04217b7218f7c36f27e293b7d1e51 //即为js私钥
a00a
06082a811ccf5501822d
a144
034200
0498024e7a2ad38f79223394aab9a30fd30be81c6c5efd307d520ff5f53d9d4bee2f7e62c843ae2c0ff448dc6e56297fda6154d5110ea246e4b692c2d3bda96949 即为js私钥
publicKey:
完整:3059301306072a8648ce3d020106082a811ccf5501822d0342000498024e7a2ad38f79223394aab9a30fd30be81c6c5efd307d520ff5f53d9d4bee2f7e62c843ae2c0ff448dc6e56297fda6154d5110ea246e4b692c2d3bda96949
拆解:
3059301306072a8648ce3d0201
06082a811ccf5501822d
034200
0498024e7a2ad38f79223394aab9a30fd30be81c6c5efd307d520ff5f53d9d4bee2f7e62c843ae2c0ff448dc6e56297fda6154d5110ea246e4b692c2d3bda96949 //js公钥
- JAVA与Node无法加解密生成的密文 这个PR让我知道JAVA密文要加去04让Node解密,反之,Node加密内容需加上04让JAVA解密。
Demo地址
总结
这个问题困扰了我两天,因为害怕自己思路出错,每天花了3小时左右,解决问题方法并不推荐,因为是野路子,翻别人PR极其花时间,最后自嘲,别人的密码学,看源码重写,自己的密码学,截取字符串一个一个试。