【国密SM2、SM3】Go、Java和 IavaScript 签名三端兼容

一 名词

1.1 SM2

2010年12月17日,国家密码管理局关于发布《SM2椭圆曲线公钥密码算法》公告

  • a) 压缩表示形式,PC = 02或03;
  • b) 未压缩表示形式,PC = 04;
  • c) 混合表示形式,PC = 06或07

1.2 SM2 椭圆曲线公钥密码算法推荐曲线参数

推荐使用素数域256位椭圆曲线

椭圆曲线方程:y2 = x3 + ax + b

曲线参数:

  • p = FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF
  • a = FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC
  • b = 28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93
  • n = FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123
  • Gx = 32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7
  • Gy = BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0

1.3 SM3

2010年12月17日,国家密码管理局关于发布《SM3密码杂凑算法》公告

1.4 Byte

  • golang 中的Byte 为 uint8,取值范围[0-255],十六进制范围[00-ff]
  • java 中的Byte 为 int8,取值范围[-128-127],十六进制范围[80-7f]

1.5 原码、反码和补码

  • 原码: 符号位0表示正,1表示负
  • 反码: 正数反码与原码一致,负数反码为原码绝对值各位求反,符号位不变
  • 补码: 正数补码与原码一致,负数补码为其反码+1
数值 原码 反码 补码 十六进制值
100 01100100 01100100 01100100 6f
-100 11100100 10011011(155) 10011100(156) 9c

1.6 ASN.1编码方式

ASN.1 – Abstract Syntax Notation dot one,抽象记法1。
所有X.509都是DER编码,DER是指ASN.1的编码规则。

二 加签

2.1 加签流程

  1. 签名内容"123456";
  2. 对内容进行sm3计算hash;
  3. 使用私钥对hash进行签名。

image

2.2 Go加签流程

uid = "12345678"
ENTLA = 8 * len(uid) = 128
  1. ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
byte((Entla >> 8) & 0xFF    # 0
Entla & 0xFF                # 128
uid                         # 49 50 51 52 53 54 55 56 49 50 51 52 53 54 55 56
a                           # 255 255 255 254 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 0 255 255 255 255 255 255 255 252
b                           # 40 233 250 158 157 159 94 52 77 90 158 75 207 101 9 167 243 151 137 245 21 171 143 146 221 188 189 65 77 148 14 147
Gx                          # 50 196 174 44 31 25 129 25 95 153 4 70 106 57 201 148 143 227 11 191 242 102 11 225 113 90 69 137 51 76 116 199
Gy                          # 188 55 54 162 244 246 119 156 89 189 206 227 107 105 33 83 208 169 135 124 198 42 71 64 2 223 50 229 33 57 240 160
PUBx                        # 252 77 110 56 65 36 41 190 145 52 178 128 97 175 201 94 157 164 52 230 179 18 147 103 59 174 212 219 53 23 52 1
PUBy                        # 162 100 33 54 2 84 181 189 131 30 55 99 247 128 42 117 91 207 209 156 21 118 13 202 52 25 192 77 197 127 128 246

Hex                         # 008031323334353637383132333435363738fffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e9332c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0fc4d6e38412429be9134b28061afc95e9da434e6b31293673baed4db35173401a26421360254b5bd831e3763f7802a755bcfd19c15760dca3419c04dc57f80f6
SM3                         # f8471dfdb4e9580d64d48ec7270593241943f34af4c00300ee354b3c7cfb6bfc
  1. msgHash = H256(za + msg)
23b1315ef7f28673d1a8b822b2638c1ddaf9bdb93e296ebe8182da955c34a1a5
  1. 根据msgHash计算签名(r,s)
func Sm2Sign(priv *PrivateKey, msg, uid []byte, random io.Reader) (r, s *big.Int, err error) {
	digest, err := priv.PublicKey.Sm3Digest(msg, uid)
	if err != nil {
		return nil, nil, err
	}
	e := new(big.Int).SetBytes(digest)
	c := priv.PublicKey.Curve
	N := c.Params().N
	if N.Sign() == 0 {
		return nil, nil, errZeroParam
	}
	var k *big.Int
	for {
		for {
			k, err = randFieldElement(c, random)
			if err != nil {
				r = nil
				return
			}
			r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
			r.Add(r, e)
			r.Mod(r, N)
			if r.Sign() != 0 {
				if t := new(big.Int).Add(r, k); t.Cmp(N) != 0 {
					break
				}
			}

		}
		rD := new(big.Int).Mul(priv.D, r)
		s = new(big.Int).Sub(k, rD)
		d1 := new(big.Int).Add(priv.D, one)
		d1Inv := new(big.Int).ModInverse(d1, N)
		s.Mul(s, d1Inv)
		s.Mod(s, N)
		if s.Sign() != 0 {
			break
		}
	}
	return
}
  1. 对签名进行ASN.1编码
asn1.Marshal(sm2Signature{r, s})

2.3 Java加签流程

uid = "12345678"
ENTLA = 8 * len(uid) = 128
  1. ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
byte((Entla >> 8) & 0xFF    # 0
Entla & 0xFF                # -128
uid                         # 49, 50, 51, 52, 53, 54, 55, 56, 49, 50, 51, 52, 53, 54, 55, 56
a                           # -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -4
b                           # 40, -23, -6, -98, -99, -97, 94, 52, 77, 90, -98, 75, -49, 101, 9, -89, -13, -105, -119, -11, 21, -85, -113, -110, -35, -68, -67, 65, 77, -108, 14, -109
Gx                          # 50, -60, -82, 44, 31, 25, -127, 25, 95, -103, 4, 70, 106, 57, -55, -108, -113, -29, 11, -65, -14, 102, 11, -31, 113, 90, 69, -119, 51, 76, 116, -57
Gy                          # -68, 55, 54, -94, -12, -10, 119, -100, 89, -67, -50, -29, 107, 105, 33, 83, -48, -87, -121, 124, -58, 42, 71, 64, 2, -33, 50, -27, 33, 57, -16, -96
PUBx                        # -4, 77, 110, 56, 65, 36, 41, -66, -111, 52, -78, -128, 97, -81, -55, 94, -99, -92, 52, -26, -77, 18, -109, 103, 59, -82, -44, -37, 53, 23, 52, 1
PUBy                        # -94, 100, 33, 54, 2, 84, -75, -67, -125, 30, 55, 99, -9, -128, 42, 117, 91, -49, -47, -100, 21, 118, 13, -54, 52, 25, -64, 77, -59, 127, -128, -10

joinByte                    # 0, -128, 49, 50, 51, 52, 53, 54, 55, 56, 49, 50, 51, 52, 53, 54, 55, 56, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -4, 40, -23, -6, -98, -99, -97, 94, 52, 77, 90, -98, 75, -49, 101, 9, -89, -13, -105, -119, -11, 21, -85, -113, -110, -35, -68, -67, 65, 77, -108, 14, -109, 50, -60, -82, 44, 31, 25, -127, 25, 95, -103, 4, 70, 106, 57, -55, -108, -113, -29, 11, -65, -14, 102, 11, -31, 113, 90, 69, -119, 51, 76, 116, -57, -68, 55, 54, -94, -12, -10, 119, -100, 89, -67, -50, -29, 107, 105, 33, 83, -48, -87, -121, 124, -58, 42, 71, 64, 2, -33, 50, -27, 33, 57, -16, -96, -4, 77, 110, 56, 65, 36, 41, -66, -111, 52, -78, -128, 97, -81, -55, 94, -99, -92, 52, -26, -77, 18, -109, 103, 59, -82, -44, -37, 53, 23, 52, 1, -94, 100, 33, 54, 2, 84, -75, -67, -125, 30, 55, 99, -9, -128, 42, 117, 91, -49, -47, -100, 21, 118, 13, -54, 52, 25, -64, 77, -59, 127, -128, -10
Hex                         # 008031323334353637383132333435363738FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E9332C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0FC4D6E38412429BE9134B28061AFC95E9DA434E6B31293673BAED4DB35173401A26421360254B5BD831E3763F7802A755BCFD19C15760DCA3419C04DC57F80F6
SM3                         # -8, 71, 29, -3, -76, -23, 88, 13, 100, -44, -114, -57, 39, 5, -109, 36, 25, 67, -13, 74, -12, -64, 3, 0, -18, 53, 75, 60, 124, -5, 107, -4
SM3 Hex                     # f8471dfdb4e9580d64d48ec7270593241943f34af4c00300ee354b3c7cfb6bfc
  • Byte 转 Hex String
private static String byteToHexString(byte b) {
    int n = b;
    if (n < 0)
        n = 256 + n;
    int d1 = n / 16;
    int d2 = n % 16;
    return "" + hexDigits[d1] + hexDigits[d2];
}
  1. msgHash = H256(za + msg)
23b1315ef7f28673d1a8b822b2638c1ddaf9bdb93e296ebe8182da955c34a1a5
  1. 根据msgHash计算签名(r,s)
public Signature sign(String M, String IDA, SM2KeyPair keyPair) {
    byte[] ZA = ZA(IDA, keyPair.getPublicKey());
    printHexString(ZA);
    byte[] M_ = join(ZA, M.getBytes());
    printHexString(ZA);
    BigInteger e = new BigInteger(1, sm3hash(M_));
    BigInteger k;
    BigInteger r;
    do {
        k = random(n);
        ECPoint p1 = G.multiply(k).normalize();
        BigInteger x1 = p1.getXCoord().toBigInteger();
        r = e.add(x1);
        r = r.mod(n);
    } while (r.equals(BigInteger.ZERO) || r.add(k).equals(n));

    BigInteger s = ((keyPair.getPrivateKey().add(BigInteger.ONE).modInverse(n))
            .multiply((k.subtract(r.multiply(keyPair.getPrivateKey()))).mod(n))).mod(n);

    return new Signature(r, s);
}
  1. 对签名进行ASN.1编码

三 验签

image

四 验证

4.1 Java

  • 私钥:bf0a4549f58d1d436764b6d758a7aabd243081d0580efa04a6707d6950669d7d
  • 公钥:039cae08b2f6f0d02cf600fd7f36698e07968571e9aa33006db24e5eb70aad6800
  • 加签内容:“123”
  • 加签结果:3046022100d3586936b2a25f435ed4f6a3da52bb4e857c9780b9a15476f2af5071a6765e20022100cc5c439547110e181856d57b4844632ae19e5cb12285e0401cad34010435d00a
  • 验签结果:true

4.2 Go

  • 私钥:bf0a4549f58d1d436764b6d758a7aabd243081d0580efa04a6707d6950669d7d
  • 公钥:039cae08b2f6f0d02cf600fd7f36698e07968571e9aa33006db24e5eb70aad6800
  • 加签内容:“123”
  • 加签结果:3045022100e96bb565d81a6752aeaaff90bbde6fc5895c2c5034379eca8beb7ec62c70b5c6022059bca73faad7bcf9db73984155775ec225047af547b99548fcf485f6cb925b6f
  • 验签结果:true

4.3 JavaScript

  • 私钥:bf0a4549f58d1d436764b6d758a7aabd243081d0580efa04a6707d6950669d7d
  • 公钥:039cae08b2f6f0d02cf600fd7f36698e07968571e9aa33006db24e5eb70aad6800
  • 加签内容:“123”

你可能感兴趣的:(杂七杂八,golang,java,开发语言)