国密 SM2 的非对称签名验签过程

国密 SM2 的非对称签名验签过程

介绍

非对称加密确保了消息传输中的保密性,但是由于使用公钥加密,而公钥是分发出去的,可能泄露,谁都可以使用公钥加密发送消息。

因此为了保证收到的消息是由对应的发送者发出的,就需要用到非对称签名和验签逻辑,发送者通过自己的私钥进行对消息进行签名,

接收方通过公钥对消息进行验证签名。非对称签名还确保了消息在传输过程中未被篡改,还有不可否认性,因为理论上只有发送者才有私钥

椭圆曲线

椭圆曲线和公钥,私钥,和 SM2 的非对称加密解密是一致的,这里再重复提一下。

椭圆曲线是由一组方程描述的点的集合:

y^2^ = x^3^ + ax + b 其中 a, b 满足 (4a^3^ + 27b^2^ ≠ 0)

SM2 定义了一个 sm2p256v1 的椭圆曲线方程

各种参数

C# 复制代码
BigInteger p = FromHex("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF");
BigInteger a = FromHex("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC");
BigInteger b = FromHex("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93");
BigInteger n = FromHex("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123");
BigInteger h = BigInteger.One;

Point G coord: (22963146547237050559479531362550074578802567295341616970375194840604139615431, 85132369209828568825618990617112496413088388631904505083283536607588877201568)

公钥,私钥

  • 私钥:

可以随机生成一个 BigInteger d,必须符合区间 [1, n - 1]

  • 公钥:

私钥 d * G(Point) 得到的一个 Point: Q

签名过程

私钥: d

userID: SM2 提供了一个默认的 userId "1234567812345678" 字符串的 byte 数组,可以修改

签名过程主要是为了得到 BigInteger r 和 BigInteger s

摘要过程

  • d * G 生成 公钥点 Q
  • 对 (userID.Length) * 8 >> 8, userID.Length, userID, 方程参数 A, B, 基点G(x,y), 公钥点 Q(x,y), 算出摘要 z
  • 对 z, 原文 算出摘要 eHash: byte[32]
  • 将 eHash 转成 BigInteger e

生成签名

  1. 随机生成一个 BigInteger k
  2. k * G 生成一个 Point(x, y) P
  3. BigInteger r = e + P.x
  4. BigInteger s = (k - r * d) / (d + 1)

发送方将 r,s 编码随着消息发送

验签过程

验签使用公钥点 Q 进行验签 对签名发过来的 BigInteger r, BigInteger s 进行验证

摘要过程

  • 对 (userID.Length) * 8 >> 8, userID.Length, userID, 方程参数 A, B, 基点G(x,y), 公钥点 Q(x,y), 算出摘要 z
  • 对 z, 原文 算出摘要 eHash: byte[32]
  • 将 eHash 转成 BigInteger e
  • 验证等式是否成立: r = e + (s * G + (r + s) * Q).x

证明过程:

Shell 复制代码
已知条件

P = k * G

r = e + P.x

s = (k - r * d) / (d + 1)

公私钥关系 d * G = Q

e + (s * G + (r + s) * Q).x = e + P.x

s * G + (r + s) * Q = P

即要证明 s * G + r * Q + s * Q = k * G

s * G + s * Q + r * Q - k * G

∵ Q = d * G

= s * G + s * d * G + r * d * G - k * G

= (1 + d) * s * G + r * d * G - k * G

∵ s = (k - r * d) / (d + 1)

= (k - r * d) * G + r * d * G - k * G

= k * G - r * d * G + r * d * G - k * G

= k * G - k * G

= 0

总结

  1. 计算过程中省略了对求同余的计算
  2. 加密保证消息传输的保密,加签保证消息是由特定发送者发出,以及消息未被篡改
  3. SM2 算法中,签名过程使用私钥运算生成两个大数 (r, s) 验证过程则使用公钥来确认签名是否有效, 消息是否被篡改
  4. 有的机构在会约定不同的 userID