【国密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
数值原码反码补码十六进制值
1000110010001100100011001006f
-1001110010010011011(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”
  • 35
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好,您的问题似乎有些不明确。您是想了解如何在 Java 或 Go 中使用国密 SM2 算法进行签名和验签,或是想了解如何进行 P7 格式的签名和验签吗?以下是两个问题的回答: 1. 如何在 Java 或 Go 中使用国密 SM2 算法进行签名和验签? 在 Java 中使用国密 SM2 算法进行签名和验签,您可以使用 BouncyCastle 库。具体实现可以参考下面的代码: Java: ```java // 加载 BouncyCastle Provider Security.addProvider(new BouncyCastleProvider()); // 生成密钥对 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC"); ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec("sm2p256v1"); keyPairGenerator.initialize(ecGenParameterSpec, new SecureRandom()); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 获取私钥和公钥 PrivateKey privateKey = keyPair.getPrivate(); PublicKey publicKey = keyPair.getPublic(); // 签名 Signature signature = Signature.getInstance("SM3withSM2", "BC"); signature.initSign(privateKey); signature.update(data); byte[] signatureBytes = signature.sign(); // 验签 signature.initVerify(publicKey); signature.update(data); boolean verifyResult = signature.verify(signatureBytes); ``` Go: ```go // 生成密钥对 privateKey, err := sm2.GenerateKey() if err != nil { log.Fatal(err) } publicKey := &privateKey.PublicKey // 签名 hash := sm3.SumSM3(data) signature, err := privateKey.Sign(rand.Reader, hash[:], nil) if err != nil { log.Fatal(err) } // 验签 verifyResult := publicKey.Verify(hash[:], signature) ``` 2. 如何进行 P7 格式的签名和验签? 在 Java 中进行 P7 格式的签名和验签,您可以使用 BouncyCastle 库。具体实现可以参考下面的代码: Java: ```java // 加载 BouncyCastle Provider Security.addProvider(new BouncyCastleProvider()); // 读取证书和私钥 KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(new FileInputStream("keystore.p12"), "password".toCharArray()); PrivateKey privateKey = (PrivateKey) keyStore.getKey("alias", "password".toCharArray()); Certificate[] certChain = keyStore.getCertificateChain("alias"); X509Certificate cert = (X509Certificate) certChain[0]; // 加载证书链 JcaCertStore jcaCertStore = new JcaCertStore(Arrays.asList(certChain)); // 签名 ContentSigner signer = new JcaContentSignerBuilder("SM3withSM2").setProvider("BC").build(privateKey); CMSTypedData cmsData = new CMSProcessableByteArray(data); CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(signer, cert)); generator.addCertificates(jcaCertStore); CMSSignedData signedData = generator.generate(cmsData, true); byte[] signedBytes = signedData.getEncoded(); // 验签 CMSSignedData cms = new CMSSignedData(signedBytes); SignerInformationStore signers = cms.getSignerInfos(); Collection<SignerInformation> c = signers.getSigners(); Iterator<SignerInformation> it = c.iterator(); if (it.hasNext()) { SignerInformation signer = it.next(); X509CertificateHolder certHolder = (X509CertificateHolder) cms.getCertificates().getMatches(signer.getSID()).iterator().next(); if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certHolder))) { return true; } } return false; ``` Go: ```go // 读取证书和私钥 pemData, err := ioutil.ReadFile("keystore.pem") if err != nil { log.Fatal(err) } blocks := pem.Decode(pemData) key, err := x509.ParsePKCS8PrivateKey(blocks.Bytes) if err != nil { log.Fatal(err) } certData, err := ioutil.ReadFile("cert.pem") if err != nil { log.Fatal(err) } cert, err := x509.ParseCertificate(certData) if err != nil { log.Fatal(err) } // 加载证书链 certs := []*x509.Certificate{cert} intermediateData, err := ioutil.ReadFile("intermediate.pem") if err == nil { for { blocks := pem.Decode(intermediateData) if blocks == nil { break } intermediateCert, err := x509.ParseCertificate(blocks.Bytes) if err != nil { log.Fatal(err) } certs = append(certs, intermediateCert) } } // 签名 hash := sm3.SumSM3(data) signedData, err := cms.NewSignedData(hash[:], key, cert, certs...) if err != nil { log.Fatal(err) } signedBytes, err := signedData.ToDER() if err != nil { log.Fatal(err) } // 验签 cms, err := cms.ParseCMS(signedBytes) if err != nil { log.Fatal(err) } signerInfos := cms.GetSignerInfos() for _, signerInfo := range signerInfos { err = signerInfo.Verify(cert) if err == nil { return true } } return false ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值