SM2数字签名算法
签名算法的作用是用于证明“这些消息由我生成并发送的”,对于发送方而言就是用私钥 d 去和发送的消息做绑定,对于接收方而言就是确定发送方真的知道私钥 d。但是私钥是不能在网络上传输的,但是可以利用椭圆曲线离散对数问题,将 d 包装成一个点坐标,接收方使用若能够使用公钥去验证出相同的坐标,就能确定消息确实真的和 d 是绑定的。
发送方使用 SM2 签名算法会得到一个 (r,s) 元组,接收方在获取了 r、s、公钥 后,使用国标设计的验证方程
来判断签名是否有效(实际情况由于接收方无法得知 k,验证的其实是 的横坐标)。很多人学习这个算法直接开始看 r、s 的表达式,其实没必要。因为在设计算法标准的时候,最先设计出来的其实是验证方程和 r,s 其实是通过方程和 r 直接求出表达式的,没有承载什么实际的设计思想。与其思考为什么 r 的式子是怎么得出来的,不如想想:为什么验证方程是它。
为什么是这个方程 ?
接收方验证签名只为了一件事:对于一个公开的公钥 ,发送方如何证明公钥是自己的私钥生成的。既然为了验证公钥,式子中肯定要存在 。然后发送方会使用一个 r 来参与运算,r 通过 SM3 杂凑算法与发送方的身份标识、私钥绑定,可以看作是私钥套了个壳。按照这个思路理论上得出的最简单的验证方程应该是
国标为了提高安全性,避免通过偶然得知 k 直接解出私钥等特殊情况,最后优化成了现在的版本,也就是增加了 s 变量。
发送方生成签名
由于签名的格式是 (r,s) 元组,需要分开探讨。
计算 r
我可以直接把 r 的计算式子写出来
计算 r 还需要一个参数 e (消息摘要),这个 e 其实就是一连串有关发送方的信息 和 明文 M 杂凑的结果。由于 e 和 都是大数,很容易就超出 SM2 规定的范围,这里的取模运算能够限制最终的结果在 上。
而这里的 就是发送方的唯一标识符 ID 和各种固定参数杂凑后的结果。
其中,ID 是发送者的身份标识,可以是身份证号、 UUID 等,前面的 ENTL 用于标识 ID 的长度。a 和 b 是椭圆曲线 对应对参数, 和 是基点的坐标,这几个参数都在国标中被规定了固定的值。和是签名者公钥,也就是通过私钥 和基点计算出的一个秘密点,也就是 。
这里为什么要把 和 e 分开求杂凑呢?因为 使用的参数基本都是不变的,可以被缓存。出于性能考虑,这样对于不同的明文也可以快速地计算出 e。
然后通过前面 SM2 公钥加密算法的学习可以知道,每次传输都希望使用不同的“临时密钥”来提高安全性,而参数 就体现了这个原则,得到它的式子是
其中 k 是由随机数生成器生成的随机数。这样,通过 e 和 就可以将发送方的信息、当前会话的标识 k 和发送的数据进行绑定。
计算 s
经过前面的对前面算法的剖析,s 这个值其实就是方程
把 r、公钥带入后得到的式子,也就是
真的没什么好讲的,就是移项计算,毕竟 s 本身就是一个用于优化算法的变量。
思考
SM2 数字签名算法借助椭圆曲线难题,巧妙地将 问题转化为了 方程,利用数学方法避免了直接传输私钥却能证明公钥的归属。