详解国密 SM2 的加密和解密

在上一篇文章《 解读国密非对称加密算法SM2 》介绍了国密非对称算法SM2,在文章中说到,如果现有的网络库中已经实现ECC算法,只需加入SM2命名曲线的参数即可。这对于ECDHE密钥协商和ECDSA数字签名这两种用途而言确实是足够的。现有的网络库,很少将ECC算法直接用于加密和解密。但在实现ECC_SM4_SM3这个密码套件中,在密钥交换过程中,存在客户端将Pre-Master Secret使用 SM2 公钥加密后传给服务器端的步骤。所以我们需要实现 SM2 的加密和解密。

如何使用 SM2 算法进行加密和解密,可以参考的资料是《GMT 0003.4-2012 SM2椭圆曲线公钥密码算法第4部分:公钥加密算法》。但这份文档有些地方描述的并不是很清楚,实现起来也是很头疼,在参考了GmSSL开源项目的源码之后,总算把这个过程梳理清楚了。先总结一下,把其中容易出错的位置重点说明一下,供大家参考。

先来看看国密文档中关于加密流程的描述:

  1. 在A1步骤中,需要注意不能使用C语言中简单的随机数函数,因为这里 k 是一个很大的数字,有32字节,在GmSSL是用 BIGNUM 结构来表示的。在一般网络库中都会定义这种大整数类型,也会提供了随机函数发生器。

  2. 在A2步骤中,一般实现了ECC算法的网络库都实现了 [k]G 这种运算,找到使用即可。将C1转化为比特串,需要考虑CPU大小端的问题,通常网络库有现成的函数。

  3. 在A3步骤中,刚开始看文档没明白 h 值是什么,后来才理解到这就是曲线参数的 cofactor,而且这个步骤主要是验证公钥PB的有效性,略过也没问题。

  4. 在A5步骤中,有个KDF函数需要实现。KDF函数的流程如下,其中的Hv函数请使用SM3:

  1. A7步骤的Hash也采用SM3

  2. 在最后拼接 C1 || C3 || C2 步骤,并不是把这些字节拼接起来就完事,我吃过大亏。因为在解密步骤中,还需要用到 x1、y1、C3、C2这几个参数,如果拼接成一个bit串,接收方如何拆分?如果我们内部使用,当然可以根据它们各自的长度(对于指定SM2曲线和SM3哈希算法,x1, y1, C3的长度是固定的)来拆分,但这样不够灵活,万一换了命名曲线或哈希算法呢?在文档中没有找到说明,但我研究了GmSSL的源码,才弄明白要采用ASN.1 DER编码,这样接受方就可以通过DER解码,分别拿到x1、y1、C3、C2参数值。关于DER编码解码,请参考相关的资料,一般网络库都会提供DER编解码的函数,我们只要调用就可以了。

最后这一步,要是只看这份文档,就会掉进一个大坑。我在本地实现了SM2的加密和解密,使用 《GMT 0003.4-2012》文档附录中的数据进行测试,也都通过,但在与第三方服务器端对接时,总是解密失败,后来才发现是因为这个原因。

国密文档中关于解密流程的描述:

实现了加密流程后,解密流程的实现就简单了,这里不再过多描述。

《GMT 0003.4-2012》文档附录A中的测试数据非常有用,有每个步骤的计算结果,可用于排查哪个步骤实现出现问题,在开发中要充分利用。

小结

非对称加密算法通常很少直接用于数据的加密和解密,主要是考虑到其速度远低于对称加密算法。在ECC_SM4_SM3密码套件中,SM2用于密钥交换(客户端生成Pre-Master Secret,加密后传输给服务器端)。当然,如果在程序中直接使用SM2加密信用卡卡号之类的小数据也是可以的。

如果大家有什么问题,欢迎交流。