使用JAVA Hutool 与NodeJS 实现SM2 加解密互相转换

3,279 阅读5分钟

背景

意外接触到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:C1C3C2C1C2C3

  • 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,最终截取结果如下:
privateKey:
完整:308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420730895115e28d7cdbc5be77e0a2c39f690e04217b7218f7c36f27e293b7d1e51a00a06082a811ccf5501822da1440342000498024e7a2ad38f79223394aab9a30fd30be81c6c5efd307d520ff5f53d9d4bee2f7e62c843ae2c0ff448dc6e56297fda6154d5110ea246e4b692c2d3bda96949
拆解:
   308193020100301306072a8648ce3d0201
   06082a811ccf5501822d
   047930770201010420
   730895115e28d7cdbc5be77e0a2c39f690e04217b7218f7c36f27e293b7d1e51  //即为js私钥
   a00a
   06082a811ccf5501822d
   a144
   034200
   0498024e7a2ad38f79223394aab9a30fd30be81c6c5efd307d520ff5f53d9d4bee2f7e62c843ae2c0ff448dc6e56297fda6154d5110ea246e4b692c2d3bda96949 即为js私钥
publicKey:
完整:3059301306072a8648ce3d020106082a811ccf5501822d0342000498024e7a2ad38f79223394aab9a30fd30be81c6c5efd307d520ff5f53d9d4bee2f7e62c843ae2c0ff448dc6e56297fda6154d5110ea246e4b692c2d3bda96949
拆解:
  3059301306072a8648ce3d0201
  06082a811ccf5501822d
  034200
  0498024e7a2ad38f79223394aab9a30fd30be81c6c5efd307d520ff5f53d9d4bee2f7e62c843ae2c0ff448dc6e56297fda6154d5110ea246e4b692c2d3bda96949 //js公钥
  1. JAVA与Node无法加解密生成的密文 这个PR让我知道JAVA密文要加去04让Node解密,反之,Node加密内容需加上04让JAVA解密。

Demo地址

github.com/luanxuechao…

总结

这个问题困扰了我两天,因为害怕自己思路出错,每天花了3小时左右,解决问题方法并不推荐,因为是野路子,翻别人PR极其花时间,最后自嘲,别人的密码学,看源码重写,自己的密码学,截取字符串一个一个试。