java bcprov 国密 依赖 jar包 版本 升级 降级 教程
关于java的国密算法原理以及sm2、sm3、sm4的演示demo,很多博主都写过。但是如果说自身项目中用到的bcprov这个依赖jar包的版本,和别人博客里演示的不一样,或者说引用了多个版本bcprov的jar包,这种情况怎么办呢?
一般有两个方案,第一个是直接全部白嫖别人的版本,这种方案不是本文所讲内容。另一种方案就是弄明白每个版本依赖包的区别,这样我们的项目就可以只保留一个版本的依赖包了,而且想要留哪一个版本也不在话下。
重要说明:
在看代码前我做一些说明,bcprov这个包在高版本中增加了一些国密相关的签名或者加密类,但是为了兼容所有的版本,所以我并没有使用高版本中才有的类,而是根据比较低的基线版本上开发实现,基线版本代码链接:基于Java的(SM2_SM3_SM4)国密算法, 加密解密工具类及测试demo
所以我在本博客中不会把所有的类代码都放上,只放关键的代码,如果有人因此喷我是cv党,那请自觉关闭本博客。
从国内的某代理中央仓库里可以查到目前bcprov的版本参考下表。
说明1:包名中jdk14的版本适用于jdk1.4,jdk15on的版本适用于jdk1.5以上,jdk15to18的版本适用于jdk1.5到jdk1.8,jdk18on的版本适用于jdk1.8以上,各位根据自己的jdk版本自行选择对应的版本。如果这说明不明白的可以在评论区提问。
说明2:早期的bcprov版本号不一定是相连的,如果各位大佬使用的版本表格中,可以评论区提问或者私信。
说明3:如果只用国密算法的sm2、sm3、sm4,是不需要用到bcprov的ext包或者util包,只用核心包就可以了。
group-Id | artifact-Id | version | 完整jar包名 |
---|---|---|---|
org.bouncycastle | bcprov-jdk15 | 1.32 | bcprov-jdk15-1.32.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15 bcprov-jdk16 | 1.38 | bcprov-jdk14-1.38.jar bcprov-jdk15-1.38.jar bcprov-jdk16-1.38.jar |
org.bouncycastle | bcprov-jdk15 bcprov-jdk16 | 1.40 | bcprov-jdk15-1.40.jar bcprov-jdk16-1.40.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15 bcprov-jdk16 | 1.43 | bcprov-jdk14-1.43.jar bcprov-jdk15-1.43.jar bcprov-jdk16-1.43.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15 bcprov-jdk16 | 1.44 | bcprov-jdk14-1.44.jar bcprov-jdk15-1.44.jar bcprov-jdk16-1.44.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15 bcprov-jdk16 | 1.44 | bcprov-jdk14-1.44.jar bcprov-jdk15-1.44.jar bcprov-jdk16-1.44.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15 bcprov-jdk16 | 1.45 | bcprov-jdk14-1.45.jar bcprov-jdk15-1.45.jar bcprov-jdk16-1.45.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15 bcprov-jdk15+ bcprov-jdk15on bcprov-jdk16 | 1.46 | bcprov-jdk14-1.46.jar bcprov-jdk15-1.46.jar bcprov-jdk15±1.46.jar bcprov-jdk15on-1.46.jar bcprov-jdk16-1.46.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.47 | bcprov-jdk14-1.47.jar bcprov-jdk15on-1.47.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.48 | bcprov-jdk14-1.48.jar bcprov-jdk15on-1.48.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.49 | bcprov-jdk14-1.49.jar bcprov-jdk15on-1.49.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.50 | bcprov-jdk14-1.50.jar bcprov-jdk15on-1.50.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.51 | bcprov-jdk14-1.51.jar bcprov-jdk15on-1.51.jar |
org.bouncycastle | bcprov-jdk15on | 1.52 | bcprov-jdk15on-1.52.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.53 | bcprov-jdk14-1.53.jar bcprov-jdk15on-1.53.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.54 | bcprov-jdk14-1.54.jar bcprov-jdk15on-1.54.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.55 | bcprov-jdk14-1.55.jar bcprov-jdk15on-1.55.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.56 | bcprov-jdk14-1.56.jar bcprov-jdk15on-1.56.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.57 | bcprov-jdk14-1.57.jar bcprov-jdk15on-1.57.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.58 | bcprov-jdk14-1.58.jar bcprov-jdk15on-1.58.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.59 | bcprov-jdk14-1.59.jar bcprov-jdk15on-1.59.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.60 | bcprov-jdk14-1.60.jar bcprov-jdk15on-1.60.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.61 | bcprov-jdk14-1.61.jar bcprov-jdk15on-1.61.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on | 1.62 | bcprov-jdk14-1.62.jar bcprov-jdk15on-1.62.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on bcprov-jdk15to18 | 1.63 | bcprov-jdk14-1.63.jar bcprov-jdk15on-1.63.jar bcprov-jdk15to18-1.63.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on bcprov-jdk15to18 | 1.64 | bcprov-jdk14-1.64.jar bcprov-jdk15on-1.64.jar bcprov-jdk15to18-1.64.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on bcprov-jdk15to18 | 1.65 | bcprov-jdk14-1.65.jar bcprov-jdk15on-1.65.jar bcprov-jdk15to18-1.65.jar |
org.bouncycastle | bcprov-jdk15on bcprov-jdk15to18 | 1.66 | bcprov-jdk15-1.66.jar bcprov-jdk16-1.66.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on bcprov-jdk15to18 | 1.67 | bcprov-jdk14-1.67.jar bcprov-jdk15on-1.67.jar bcprov-jdk15to18-1.67.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on bcprov-jdk15to18 | 1.68 | bcprov-jdk14-1.68.jar bcprov-jdk15on-1.68.jar bcprov-jdk15to18-1.68.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on bcprov-jdk15to18 | 1.69 | bcprov-jdk14-1.69.jar bcprov-jdk15on-1.69.jar bcprov-jdk15to18-1.69.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15on bcprov-jdk15to18 | 1.70 | bcprov-jdk14-1.70.jar bcprov-jdk15on-1.70.jar bcprov-jdk15to18-1.70.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15to18 bcprov-jdk18on | 1.71 | bcprov-jdk14-1.71.jar bcprov-jdk15to18-1.71.jar bcprov-jdk18on-1.71.jar |
org.bouncycastle | bcprov-jdk14 bcprov-jdk15to18 bcprov-jdk18on | 1.72 | bcprov-jdk14-1.72.jar bcprov-jdk15to18-1.72.jar bcprov-jdk18on-1.72.jar |
更新说明
2023-06-27 增加1.73-1.75版本,且从1.74开始、不再支持jdk1.4版本,至少要jdk1.5及以上。
group-Id | artifact-Id | version | 完整jar包名 |
---|---|---|---|
org.bouncycastle | bcprov-jdk14 bcprov-jdk15to18 bcprov-jdk18on | 1.73 | bcprov-jdk14-1.73.jar bcprov-jdk15to18-1.73.jar bcprov-jdk18on-1.73.jar |
org.bouncycastle | bcprov-jdk15to18 bcprov-jdk18on | 1.74 | bcprov-jdk15to18-1.74.jar bcprov-jdk18on-1.74.jar |
org.bouncycastle | bcprov-jdk15to18 bcprov-jdk18on | 1.75 | bcprov-jdk15to18-1.75.jar bcprov-jdk18on-1.75.jar |
上述表格中的版本,博主除了最老的1.32版本没有搞,其他的版本都测试过,测试使用的jdk1.8,其他版本jdk自行测试,下面会把上述jar包版本分成4个区间,也就是在区间内的任意一个版本都可以使用对应的代码。
1、基线版本
博客开头已经说明,博主原始国密算法实现用的bcprov版本是1.45,所以这里先展示基线的关键代码,方便各位对比。
(1)引入依赖
org.bouncycastle bcprov-jdk15on 1.45
(2)关键代码
这里只放三个类的代码,Cipher、SM2和SM2Utils,对于不同版本bcprov最多只需要改这三个类,所以其他类就不贴了,各位要所有类的代码,看上述的链接就行。
Cipher
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;import org.bouncycastle.crypto.params.ECPrivateKeyParameters;import org.bouncycastle.crypto.params.ECPublicKeyParameters;import org.bouncycastle.math.ec.ECPoint;import java.math.BigInteger;public class Cipher{ private int ct; private ECPoint p2; private SM3Digest sm3keybase; private SM3Digest sm3c3; private byte[] key; private byte keyOff; public Cipher() { this.ct = 1; this.key = new byte[32]; this.keyOff = 0; } private void Reset() { this.sm3keybase = new SM3Digest(); this.sm3c3 = new SM3Digest(); byte[] p = Util.byteConvert32Bytes(p2.getX().toBigInteger()); this.sm3keybase.update(p, 0, p.length); this.sm3c3.update(p, 0, p.length); p = Util.byteConvert32Bytes(p2.getY().toBigInteger()); this.sm3keybase.update(p, 0, p.length); this.ct = 1; NextKey(); } private void NextKey() { SM3Digest sm3keycur = new SM3Digest(this.sm3keybase); sm3keycur.update((byte) (ct >> 24 & 0xff)); sm3keycur.update((byte) (ct >> 16 & 0xff)); sm3keycur.update((byte) (ct >> 8 & 0xff)); sm3keycur.update((byte) (ct & 0xff)); sm3keycur.doFinal(key, 0); this.keyOff = 0; this.ct++; } public ECPoint Init_enc(SM2 sm2, ECPoint userKey) { AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair(); ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate(); ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic(); BigInteger k = ecpriv.getD(); ECPoint c1 = ecpub.getQ(); this.p2 = userKey.multiply(k); Reset(); return c1; } public void Encrypt(byte[] data) { this.sm3c3.update(data, 0, data.length); for (int i = 0; i < data.length; i++) { if (keyOff == key.length) { NextKey(); } data[i] ^= key[keyOff++]; } } public void Init_dec(BigInteger userD, ECPoint c1) { this.p2 = c1.multiply(userD); Reset(); } public void Decrypt(byte[] data) { for (int i = 0; i < data.length; i++) { if (keyOff == key.length) { NextKey(); } data[i] ^= key[keyOff++]; } this.sm3c3.update(data, 0, data.length); } public void Dofinal(byte[] c3) { byte[] p = Util.byteConvert32Bytes(p2.getY().toBigInteger()); this.sm3c3.update(p, 0, p.length); this.sm3c3.doFinal(c3, 0); Reset(); }}
SM2
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;import org.bouncycastle.crypto.generators.ECKeyPairGenerator;import org.bouncycastle.crypto.params.ECDomainParameters;import org.bouncycastle.crypto.params.ECKeyGenerationParameters;import org.bouncycastle.crypto.params.ECPrivateKeyParameters;import org.bouncycastle.crypto.params.ECPublicKeyParameters;import org.bouncycastle.math.ec.ECCurve;import org.bouncycastle.math.ec.ECFieldElement;import org.bouncycastle.math.ec.ECFieldElement.Fp;import org.bouncycastle.math.ec.ECPoint;import java.math.BigInteger;import java.security.SecureRandom;public class SM2{ //测试参数//public static final String[] ecc_param = {//"8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3",//"787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498",//"63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A",//"8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7",//"421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D",//"0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2"//};// 正式参数 为国密算法推荐参数public static String[] ecc_param = {"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF","FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC","28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93","FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123","32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7","BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"};public static SM2 Instance(){return new SM2();}public final BigInteger ecc_p;public final BigInteger ecc_a;public final BigInteger ecc_b;public final BigInteger ecc_n;public final BigInteger ecc_gx;public final BigInteger ecc_gy;public final ECCurve ecc_curve;public final ECPoint ecc_point_g;public final ECDomainParameters ecc_bc_spec;public final ECKeyPairGenerator ecc_key_pair_generator;public final ECFieldElement ecc_gx_fieldelement;public final ECFieldElement ecc_gy_fieldelement;public SM2(){this.ecc_p = new BigInteger(ecc_param[0], 16);this.ecc_a = new BigInteger(ecc_param[1], 16);this.ecc_b = new BigInteger(ecc_param[2], 16);this.ecc_n = new BigInteger(ecc_param[3], 16);this.ecc_gx = new BigInteger(ecc_param[4], 16);this.ecc_gy = new BigInteger(ecc_param[5], 16);this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);ECKeyGenerationParameters ecc_ecgenparam;ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());this.ecc_key_pair_generator = new ECKeyPairGenerator();this.ecc_key_pair_generator.init(ecc_ecgenparam);}public byte[] sm2GetZ(byte[] userId, ECPoint userKey){SM3Digest sm3 = new SM3Digest();int len = userId.length * 8;sm3.update((byte) (len >> 8 & 0xFF));sm3.update((byte) (len & 0xFF));sm3.update(userId, 0, userId.length);byte[] p = Util.byteConvert32Bytes(ecc_a);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(ecc_b);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(ecc_gx);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(ecc_gy);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(userKey.getX().toBigInteger());sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(userKey.getY().toBigInteger());sm3.update(p, 0, p.length);byte[] md = new byte[sm3.getDigestSize()];sm3.doFinal(md, 0);return md;}public void sm2Sign(byte[] md, BigInteger userD, ECPoint userKey, SM2Result sm2Result){BigInteger e = new BigInteger(1, md);BigInteger k = null;ECPoint kp = null;BigInteger r = null;BigInteger s = null;do{do{// 正式环境AsymmetricCipherKeyPair keypair = ecc_key_pair_generator.generateKeyPair();ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) keypair.getPrivate();ECPublicKeyParameters ecpub = (ECPublicKeyParameters) keypair.getPublic();k = ecpriv.getD();kp = ecpub.getQ();// 国密规范测试 随机数k//String kS = "6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F";//k = new BigInteger(kS, 16);//kp = this.ecc_point_g.multiply(k);//System.out.println("计算曲线点X1: " + kp.getX().toBigInteger().toString(16));//System.out.println("计算曲线点Y1: " + kp.getY().toBigInteger().toString(16));//System.out.println("");// rr = e.add(kp.getX().toBigInteger());r = r.mod(ecc_n);} while (r.equals(BigInteger.ZERO) || r.add(k).equals(ecc_n));// (1 + dA)~-1BigInteger da_1 = userD.add(BigInteger.ONE);da_1 = da_1.modInverse(ecc_n);// ss = r.multiply(userD);s = k.subtract(s).mod(ecc_n);s = da_1.multiply(s).mod(ecc_n);} while (s.equals(BigInteger.ZERO));sm2Result.r = r;sm2Result.s = s;}public void sm2Verify(byte[] md, ECPoint userKey, BigInteger r, BigInteger s, SM2Result sm2Result){sm2Result.R = null;BigInteger e = new BigInteger(1, md);BigInteger t = r.add(s).mod(ecc_n);if(t.equals(BigInteger.ZERO)){return;}else{ECPoint x1y1 = ecc_point_g.multiply(sm2Result.s);//System.out.println("计算曲线点X0: " + x1y1.getX().toBigInteger().toString(16));//System.out.println("计算曲线点Y0: " + x1y1.getY().toBigInteger().toString(16));//System.out.println("");x1y1 = x1y1.add(userKey.multiply(t));//System.out.println("计算曲线点X1: " + x1y1.getX().toBigInteger().toString(16));//System.out.println("计算曲线点Y1: " + x1y1.getY().toBigInteger().toString(16));//System.out.println("");sm2Result.R = e.add(x1y1.getX().toBigInteger()).mod(ecc_n);System.out.println("R: " + sm2Result.R.toString(16));return;}}}
SM2Utils
import org.bouncycastle.asn1.*;import org.bouncycastle.math.ec.ECPoint;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.math.BigInteger;import java.util.Enumeration;public class SM2Utils{public static byte[] encrypt(byte[] publicKey, byte[] data) throws IOException{if (publicKey == null || publicKey.length == 0){return null;}if (data == null || data.length == 0){return null;}byte[] source = new byte[data.length];System.arraycopy(data, 0, source, 0, data.length);Cipher cipher = new Cipher();SM2 sm2 = SM2.Instance();ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);ECPoint c1 = cipher.Init_enc(sm2, userKey);cipher.Encrypt(source);byte[] c3 = new byte[32];cipher.Dofinal(c3);DERInteger x = new DERInteger(c1.getX().toBigInteger());DERInteger y = new DERInteger(c1.getY().toBigInteger());DEROctetString derDig = new DEROctetString(c3);DEROctetString derEnc = new DEROctetString(source);ASN1EncodableVector v = new ASN1EncodableVector();v.add(x);v.add(y);v.add(derDig);v.add(derEnc);DERSequence seq = new DERSequence(v);ByteArrayOutputStream bos = new ByteArrayOutputStream();DEROutputStream dos = new DEROutputStream(bos);dos.writeObject(seq);return bos.toByteArray();}public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException{if (privateKey == null || privateKey.length == 0){return null;}if (encryptedData == null || encryptedData.length == 0){return null;}byte[] enc = new byte[encryptedData.length];System.arraycopy(encryptedData, 0, enc, 0, encryptedData.length);SM2 sm2 = SM2.Instance();BigInteger userD = new BigInteger(1, privateKey);ByteArrayInputStream bis = new ByteArrayInputStream(enc);ASN1InputStream dis = new ASN1InputStream(bis);DERObject derObj = dis.readObject();ASN1Sequence asn1 = (ASN1Sequence) derObj;DERInteger x = (DERInteger) asn1.getObjectAt(0);DERInteger y = (DERInteger) asn1.getObjectAt(1);ECPoint c1 = sm2.ecc_curve.createPoint(x.getValue(), y.getValue(), true);Cipher cipher = new Cipher();cipher.Init_dec(userD, c1);DEROctetString data = (DEROctetString) asn1.getObjectAt(3);enc = data.getOctets();cipher.Decrypt(enc);byte[] c3 = new byte[32];cipher.Dofinal(c3);return enc;}public static byte[] sign(byte[] userId, byte[] privateKey, byte[] sourceData) throws IOException{if (privateKey == null || privateKey.length == 0){return null;}if (sourceData == null || sourceData.length == 0){return null;}SM2 sm2 = SM2.Instance();BigInteger userD = new BigInteger(privateKey);//System.out.println("userD: " + userD.toString(16));//System.out.println("");ECPoint userKey = sm2.ecc_point_g.multiply(userD);//System.out.println("椭圆曲线点X: " + userKey.getX().toBigInteger().toString(16));//System.out.println("椭圆曲线点Y: " + userKey.getY().toBigInteger().toString(16));//System.out.println("");SM3Digest sm3 = new SM3Digest();byte[] z = sm2.sm2GetZ(userId, userKey);//System.out.println("SM3摘要Z: " + Util.getHexString(z));//System.out.println("");////System.out.println("M: " + Util.getHexString(sourceData));//System.out.println("");sm3.update(z, 0, z.length);sm3.update(sourceData, 0, sourceData.length);byte[] md = new byte[32];sm3.doFinal(md, 0);//System.out.println("SM3摘要值: " + Util.getHexString(md));//System.out.println("");SM2Result sm2Result = new SM2Result();sm2.sm2Sign(md, userD, userKey, sm2Result);//System.out.println("r: " + sm2Result.r.toString(16));//System.out.println("s: " + sm2Result.s.toString(16));//System.out.println("");DERInteger d_r = new DERInteger(sm2Result.r);DERInteger d_s = new DERInteger(sm2Result.s);ASN1EncodableVector v2 = new ASN1EncodableVector();v2.add(d_r);v2.add(d_s);DERObject sign = new DERSequence(v2);byte[] signdata = sign.getDEREncoded();return signdata;}@SuppressWarnings("unchecked")public static boolean verifySign(byte[] userId, byte[] publicKey, byte[] sourceData, byte[] signData) throws IOException{if (publicKey == null || publicKey.length == 0){return false;}if (sourceData == null || sourceData.length == 0){return false;}SM2 sm2 = SM2.Instance();ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);SM3Digest sm3 = new SM3Digest();byte[] z = sm2.sm2GetZ(userId, userKey);sm3.update(z, 0, z.length);sm3.update(sourceData, 0, sourceData.length);byte[] md = new byte[32];sm3.doFinal(md, 0);System.out.println("SM3摘要值: " + Util.getHexString(md));System.out.println();ByteArrayInputStream bis = new ByteArrayInputStream(signData);ASN1InputStream dis = new ASN1InputStream(bis);DERObject derObj = dis.readObject();Enumeration e = ((ASN1Sequence) derObj).getObjects();BigInteger r = e.nextElement().getValue();BigInteger s = e.nextElement().getValue();SM2Result sm2Result = new SM2Result();sm2Result.r = r;sm2Result.s = s;//System.out.println("r: " + sm2Result.r.toString(16));//System.out.println("s: " + sm2Result.s.toString(16));//System.out.println("");sm2.sm2Verify(md, userKey, sm2Result.r, sm2Result.s, sm2Result);return sm2Result.r.equals(sm2Result.R);}}
2、版本区间1.48-1.59
之所以先写这个版本区间,是因为博主一开始用的版本是1.45改的1.55,所以就讲这个。各位使用的bcprov的版本在1.49到1.59之间都可以参考。
(1)引入依赖
我只演示区间内的其中一个版本,其他版本可自行替换测试,注意jdk版本。
org.bouncycastle bcprov-jdk15on 1.55
(2)关键代码
Cipher
同基线版本就可以,不再重复。
SM2
同基线版本就可以,不再重复。
SM2Utils
import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.bouncycastle.asn1.*;import org.bouncycastle.math.ec.ECPoint;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.math.BigInteger;import java.util.Enumeration;public class SM2Utils{private static Log log = LogFactory.getLog(SM2Utils.class);public static byte[] encrypt(byte[] publicKey, byte[] data) throws IOException{if (publicKey == null || publicKey.length == 0){return null;}if (data == null || data.length == 0){return null;}byte[] source = new byte[data.length];System.arraycopy(data, 0, source, 0, data.length);Cipher cipher = new Cipher();SM2 sm2 = SM2.Instance();ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);ECPoint c1 = cipher.Init_enc(sm2, userKey);cipher.Encrypt(source);byte[] c3 = new byte[32];cipher.Dofinal(c3);DERInteger x = new DERInteger(c1.getX().toBigInteger());DERInteger y = new DERInteger(c1.getY().toBigInteger());DEROctetString derDig = new DEROctetString(c3);DEROctetString derEnc = new DEROctetString(source);ASN1EncodableVector v = new ASN1EncodableVector();v.add(x);v.add(y);v.add(derDig);v.add(derEnc);DERSequence seq = new DERSequence(v);ByteArrayOutputStream bos = new ByteArrayOutputStream();DEROutputStream dos = new DEROutputStream(bos);dos.writeObject(seq);return bos.toByteArray();}public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException{if (privateKey == null || privateKey.length == 0){return null;}if (encryptedData == null || encryptedData.length == 0){return null;}byte[] enc = new byte[encryptedData.length];System.arraycopy(encryptedData, 0, enc, 0, encryptedData.length);SM2 sm2 = SM2.Instance();BigInteger userD = new BigInteger(1, privateKey);ByteArrayInputStream bis = new ByteArrayInputStream(enc);ASN1InputStream dis = new ASN1InputStream(bis);// 使用ASN1Object代替DERObject,ASN1Integer代替DERInteger// DERObject derObj = dis.readObject();// ASN1Sequence asn1 = (ASN1Sequence) derObj;// DERInteger x = (DERInteger) asn1.getObjectAt(0);// DERInteger y = (DERInteger) asn1.getObjectAt(1);ASN1Object derObj = (ASN1Object) dis.readObject();ASN1Sequence asn1 = (ASN1Sequence) derObj;ASN1Integer x = (ASN1Integer) asn1.getObjectAt(0);ASN1Integer y = (ASN1Integer) asn1.getObjectAt(1);ECPoint c1 = sm2.ecc_curve.createPoint(x.getValue(), y.getValue(), true);Cipher cipher = new Cipher();cipher.Init_dec(userD, c1);DEROctetString data = (DEROctetString) asn1.getObjectAt(3);enc = data.getOctets();cipher.Decrypt(enc);byte[] c3 = new byte[32];cipher.Dofinal(c3);return enc;}public static byte[] sign(byte[] userId, byte[] privateKey, byte[] sourceData) throws IOException{if (privateKey == null || privateKey.length == 0){return null;}if (sourceData == null || sourceData.length == 0){return null;}SM2 sm2 = SM2.Instance();BigInteger userD = new BigInteger(privateKey);//System.out.println("userD: " + userD.toString(16));//System.out.println("");ECPoint userKey = sm2.ecc_point_g.multiply(userD);//System.out.println("椭圆曲线点X: " + userKey.getX().toBigInteger().toString(16));//System.out.println("椭圆曲线点Y: " + userKey.getY().toBigInteger().toString(16));//System.out.println("");SM3Digest sm3 = new SM3Digest();byte[] z = sm2.sm2GetZ(userId, userKey);//System.out.println("SM3摘要Z: " + Util.getHexString(z));//System.out.println("");////System.out.println("M: " + Util.getHexString(sourceData));//System.out.println("");sm3.update(z, 0, z.length);sm3.update(sourceData, 0, sourceData.length);byte[] md = new byte[32];sm3.doFinal(md, 0);//System.out.println("SM3摘要值: " + Util.getHexString(md));//System.out.println("");SM2Result sm2Result = new SM2Result();sm2.sm2Sign(md, userD, userKey, sm2Result);//System.out.println("r: " + sm2Result.r.toString(16));//System.out.println("s: " + sm2Result.s.toString(16));//System.out.println("");DERInteger d_r = new DERInteger(sm2Result.r);DERInteger d_s = new DERInteger(sm2Result.s);ASN1EncodableVector v2 = new ASN1EncodableVector();v2.add(d_r);v2.add(d_s);// 调整DER编码的写法// DERObject sign = new DERSequence(v2);// byte[] signdata = sign.getDEREncoded();ASN1Object sign = new DERSequence(v2);byte[] signdata = sign.getEncoded("DER");return signdata;}@SuppressWarnings("unchecked")public static boolean verifySign(byte[] userId, byte[] publicKey, byte[] sourceData, byte[] signData) throws IOException{if (publicKey == null || publicKey.length == 0){return false;}if (sourceData == null || sourceData.length == 0){return false;}SM2 sm2 = SM2.Instance();ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);SM3Digest sm3 = new SM3Digest();byte[] z = sm2.sm2GetZ(userId, userKey);sm3.update(z, 0, z.length);sm3.update(sourceData, 0, sourceData.length);byte[] md = new byte[32];sm3.doFinal(md, 0);if (log.isInfoEnabled()){log.info("SM3摘要值: " + Util.getHexString(md));}//System.out.println("SM3摘要值: " + Util.getHexString(md));//System.out.println();ByteArrayInputStream bis = new ByteArrayInputStream(signData);ASN1InputStream dis = new ASN1InputStream(bis);// 使用ASN1Object替换DERObject,ASN1Integer替换DERInteger// DERObject derObj = dis.readObject();// Enumeration e = ((ASN1Sequence) derObj).getObjects();ASN1Object derObj = (ASN1Object) dis.readObject();Enumeration e = ((ASN1Sequence) derObj).getObjects();BigInteger r = e.nextElement().getValue();BigInteger s = e.nextElement().getValue();SM2Result sm2Result = new SM2Result();sm2Result.r = r;sm2Result.s = s;//System.out.println("r: " + sm2Result.r.toString(16));//System.out.println("s: " + sm2Result.s.toString(16));//System.out.println("");sm2.sm2Verify(md, userKey, sm2Result.r, sm2Result.s, sm2Result);return sm2Result.r.equals(sm2Result.R);}}
3、版本区间1.38-1.47
仓库不存在1.39、1.41和1.42版本,另外1.32无法适配,这个问题在上面也有说过,如果想用1.32版本但是不会降级的小伙伴也可以评论区咨询。
(1)引入依赖
我只演示区间内的其中一个版本,其他版本可自行替换测试,注意jdk版本。
org.bouncycastle bcprov-jdk15 1.45
(2)关键代码
Cipher
同基线版本就可以,不再重复。
SM2
同基线版本就可以,不再重复。
SM2Utils
同基线版本就可以,不再重复。
4、版本区间1.50-1.63
有些小伙伴可能会很奇怪,上面1.49-1.59不是包含了1.48-1.59了吗,为什么这里还有呢?其实也很好理解,毕竟实现方法不止一种,所以说版本在1.50-1.59之间的可以用上面的方法,也可以用这里的方法。
(1)引入依赖
我只演示区间内的其中一个版本,其他版本可自行替换测试,注意jdk版本。
org.bouncycastle bcprov-jdk15on 1.60
(2)关键代码
Cipher
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;import org.bouncycastle.crypto.params.ECPrivateKeyParameters;import org.bouncycastle.crypto.params.ECPublicKeyParameters;import org.bouncycastle.math.ec.ECPoint;import java.math.BigInteger;public class Cipher{ private int ct; private ECPoint p2; private SM3Digest sm3keybase; private SM3Digest sm3c3; private byte[] key; private byte keyOff; public Cipher() { this.ct = 1; this.key = new byte[32]; this.keyOff = 0; } private void Reset() { this.sm3keybase = new SM3Digest(); this.sm3c3 = new SM3Digest(); byte[] p = Util.byteConvert32Bytes(p2.normalize().getXCoord().toBigInteger()); this.sm3keybase.update(p, 0, p.length); this.sm3c3.update(p, 0, p.length); p = Util.byteConvert32Bytes(p2.normalize().getYCoord().toBigInteger()); this.sm3keybase.update(p, 0, p.length); this.ct = 1; NextKey(); } private void NextKey() { SM3Digest sm3keycur = new SM3Digest(this.sm3keybase); sm3keycur.update((byte) (ct >> 24 & 0xff)); sm3keycur.update((byte) (ct >> 16 & 0xff)); sm3keycur.update((byte) (ct >> 8 & 0xff)); sm3keycur.update((byte) (ct & 0xff)); sm3keycur.doFinal(key, 0); this.keyOff = 0; this.ct++; } public ECPoint Init_enc(SM2 sm2, ECPoint userKey) { AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair(); ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate(); ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic(); BigInteger k = ecpriv.getD(); ECPoint c1 = ecpub.getQ(); this.p2 = userKey.multiply(k); Reset(); return c1; } public void Encrypt(byte[] data) { this.sm3c3.update(data, 0, data.length); for (int i = 0; i < data.length; i++) { if (keyOff == key.length) { NextKey(); } data[i] ^= key[keyOff++]; } } public void Init_dec(BigInteger userD, ECPoint c1) { this.p2 = c1.multiply(userD); Reset(); } public void Decrypt(byte[] data) { for (int i = 0; i < data.length; i++) { if (keyOff == key.length) { NextKey(); } data[i] ^= key[keyOff++]; } this.sm3c3.update(data, 0, data.length); } public void Dofinal(byte[] c3) { byte[] p = Util.byteConvert32Bytes(p2.normalize().getYCoord().toBigInteger()); this.sm3c3.update(p, 0, p.length); this.sm3c3.doFinal(c3, 0); Reset(); }}
SM2
import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.math.BigInteger;import java.security.SecureRandom;public class SM2{ //测试参数//public static final String[] ecc_param = {//"8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3",//"787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498",//"63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A",//"8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7",//"421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D",//"0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2"//};// 正式参数 为国密算法推荐参数public static String[] ecc_param = {"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF","FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC","28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93","FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123","32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7","BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"};public static SM2 Instance(){return new SM2();}public final BigInteger ecc_p;public final BigInteger ecc_a;public final BigInteger ecc_b;public final BigInteger ecc_n;public final BigInteger ecc_gx;public final BigInteger ecc_gy;public final ECCurve ecc_curve;public final ECPoint ecc_point_g;public final ECDomainParameters ecc_bc_spec;public final ECKeyPairGenerator ecc_key_pair_generator;public final ECFieldElement ecc_gx_fieldelement;public final ECFieldElement ecc_gy_fieldelement;public SM2(){this.ecc_p = new BigInteger(ecc_param[0], 16);this.ecc_a = new BigInteger(ecc_param[1], 16);this.ecc_b = new BigInteger(ecc_param[2], 16);this.ecc_n = new BigInteger(ecc_param[3], 16);this.ecc_gx = new BigInteger(ecc_param[4], 16);this.ecc_gy = new BigInteger(ecc_param[5], 16);this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);// 使用反射调用方法ECPoint ecc_point_g1 = null;try {// 此区间的bcprov版本的构造方法是私有的,无法直接new,只能通过反射来调用构造方法Class clazz = ECPoint.Fp.class;Constructor c1 = clazz.getDeclaredConstructor(new Class[]{ECCurve.class, ECFieldElement.class, ECFieldElement.class, boolean.class});c1.setAccessible(true);ecc_point_g1 = (ECPoint.Fp)c1.newInstance(new Object[]{this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement, false});} catch (NoSuchMethodException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}this.ecc_point_g = ecc_point_g1;this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);ECKeyGenerationParameters ecc_ecgenparam;ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());this.ecc_key_pair_generator = new ECKeyPairGenerator();this.ecc_key_pair_generator.init(ecc_ecgenparam);}public byte[] sm2GetZ(byte[] userId, ECPoint userKey){SM3Digest sm3 = new SM3Digest();int len = userId.length * 8;sm3.update((byte) (len >> 8 & 0xFF));sm3.update((byte) (len & 0xFF));sm3.update(userId, 0, userId.length);byte[] p = Util.byteConvert32Bytes(ecc_a);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(ecc_b);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(ecc_gx);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(ecc_gy);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(userKey.normalize().getXCoord().toBigInteger());sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(userKey.normalize().getYCoord().toBigInteger());sm3.update(p, 0, p.length);byte[] md = new byte[sm3.getDigestSize()];sm3.doFinal(md, 0);return md;}public void sm2Sign(byte[] md, BigInteger userD, ECPoint userKey, SM2Result sm2Result){BigInteger e = new BigInteger(1, md);BigInteger k = null;ECPoint kp = null;BigInteger r = null;BigInteger s = null;do{do{// 正式环境AsymmetricCipherKeyPair keypair = ecc_key_pair_generator.generateKeyPair();ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) keypair.getPrivate();ECPublicKeyParameters ecpub = (ECPublicKeyParameters) keypair.getPublic();k = ecpriv.getD();kp = ecpub.getQ();// 国密规范测试 随机数k//String kS = "6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F";//k = new BigInteger(kS, 16);//kp = this.ecc_point_g.multiply(k);//System.out.println("计算曲线点X1: " + kp.getX().toBigInteger().toString(16));//System.out.println("计算曲线点Y1: " + kp.getY().toBigInteger().toString(16));//System.out.println("");// rr = e.add(kp.normalize().getXCoord().toBigInteger());r = r.mod(ecc_n);} while (r.equals(BigInteger.ZERO) || r.add(k).equals(ecc_n));// (1 + dA)~-1BigInteger da_1 = userD.add(BigInteger.ONE);da_1 = da_1.modInverse(ecc_n);// ss = r.multiply(userD);s = k.subtract(s).mod(ecc_n);s = da_1.multiply(s).mod(ecc_n);} while (s.equals(BigInteger.ZERO));sm2Result.r = r;sm2Result.s = s;}public void sm2Verify(byte[] md, ECPoint userKey, BigInteger r, BigInteger s, SM2Result sm2Result){sm2Result.R = null;BigInteger e = new BigInteger(1, md);BigInteger t = r.add(s).mod(ecc_n);if(t.equals(BigInteger.ZERO)){return;}else{ECPoint x1y1 = ecc_point_g.multiply(sm2Result.s);//System.out.println("计算曲线点X0: " + x1y1.getX().toBigInteger().toString(16));//System.out.println("计算曲线点Y0: " + x1y1.getY().toBigInteger().toString(16));//System.out.println("");x1y1 = x1y1.add(userKey.multiply(t));//System.out.println("计算曲线点X1: " + x1y1.getX().toBigInteger().toString(16));//System.out.println("计算曲线点Y1: " + x1y1.getY().toBigInteger().toString(16));//System.out.println("");sm2Result.R = e.add(x1y1.normalize().getXCoord().toBigInteger()).mod(ecc_n);//System.out.println("R: " + sm2Result.R.toString(16));return;}}}
SM2Utils
import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.bouncycastle.asn1.*;import org.bouncycastle.math.ec.ECPoint;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.math.BigInteger;import java.util.Enumeration;public class SM2Utils{private static Log log = LogFactory.getLog(SM2Utils.class);public static byte[] encrypt(byte[] publicKey, byte[] data) throws IOException{if (publicKey == null || publicKey.length == 0){return null;}if (data == null || data.length == 0){return null;}byte[] source = new byte[data.length];System.arraycopy(data, 0, source, 0, data.length);Cipher cipher = new Cipher();SM2 sm2 = SM2.Instance();ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);ECPoint c1 = cipher.Init_enc(sm2, userKey);cipher.Encrypt(source);byte[] c3 = new byte[32];cipher.Dofinal(c3);DERInteger x = new DERInteger(c1.normalize().getXCoord().toBigInteger());DERInteger y = new DERInteger(c1.normalize().getYCoord().toBigInteger());DEROctetString derDig = new DEROctetString(c3);DEROctetString derEnc = new DEROctetString(source);ASN1EncodableVector v = new ASN1EncodableVector();v.add(x);v.add(y);v.add(derDig);v.add(derEnc);DERSequence seq = new DERSequence(v);ByteArrayOutputStream bos = new ByteArrayOutputStream();DEROutputStream dos = new DEROutputStream(bos);dos.writeObject(seq);return bos.toByteArray();}public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException{if (privateKey == null || privateKey.length == 0){return null;}if (encryptedData == null || encryptedData.length == 0){return null;}byte[] enc = new byte[encryptedData.length];System.arraycopy(encryptedData, 0, enc, 0, encryptedData.length);SM2 sm2 = SM2.Instance();BigInteger userD = new BigInteger(1, privateKey);ByteArrayInputStream bis = new ByteArrayInputStream(enc);ASN1InputStream dis = new ASN1InputStream(bis);// 使用ASN1Object代替DERObject,ASN1Integer代替ASN1Object// DERObject derObj = dis.readObject();// ASN1Sequence asn1 = (ASN1Sequence) derObj;// DERInteger x = (DERInteger) asn1.getObjectAt(0);// DERInteger y = (DERInteger) asn1.getObjectAt(1);ASN1Object derObj = (ASN1Object) dis.readObject();ASN1Sequence asn1 = (ASN1Sequence) derObj;ASN1Integer x = (ASN1Integer) asn1.getObjectAt(0);ASN1Integer y = (ASN1Integer) asn1.getObjectAt(1);ECPoint c1 = sm2.ecc_curve.createPoint(x.getValue(), y.getValue());Cipher cipher = new Cipher();cipher.Init_dec(userD, c1);DEROctetString data = (DEROctetString) asn1.getObjectAt(3);enc = data.getOctets();cipher.Decrypt(enc);byte[] c3 = new byte[32];cipher.Dofinal(c3);return enc;}public static byte[] sign(byte[] userId, byte[] privateKey, byte[] sourceData) throws IOException{if (privateKey == null || privateKey.length == 0){return null;}if (sourceData == null || sourceData.length == 0){return null;}SM2 sm2 = SM2.Instance();BigInteger userD = new BigInteger(privateKey);//System.out.println("userD: " + userD.toString(16));//System.out.println("");ECPoint userKey = sm2.ecc_point_g.multiply(userD);//System.out.println("椭圆曲线点X: " + userKey.getX().toBigInteger().toString(16));//System.out.println("椭圆曲线点Y: " + userKey.getY().toBigInteger().toString(16));//System.out.println("");SM3Digest sm3 = new SM3Digest();byte[] z = sm2.sm2GetZ(userId, userKey);//System.out.println("SM3摘要Z: " + Util.getHexString(z));//System.out.println("");////System.out.println("M: " + Util.getHexString(sourceData));//System.out.println("");sm3.update(z, 0, z.length);sm3.update(sourceData, 0, sourceData.length);byte[] md = new byte[32];sm3.doFinal(md, 0);//System.out.println("SM3摘要值: " + Util.getHexString(md));//System.out.println("");SM2Result sm2Result = new SM2Result();sm2.sm2Sign(md, userD, userKey, sm2Result);//System.out.println("r: " + sm2Result.r.toString(16));//System.out.println("s: " + sm2Result.s.toString(16));//System.out.println("");DERInteger d_r = new DERInteger(sm2Result.r);DERInteger d_s = new DERInteger(sm2Result.s);ASN1EncodableVector v2 = new ASN1EncodableVector();v2.add(d_r);v2.add(d_s);// 调整DER编码的写法// DERObject sign = new DERSequence(v2);// byte[] signdata = sign.getDEREncoded();ASN1Object sign = new DERSequence(v2);byte[] signdata = sign.getEncoded("DER");return signdata;}@SuppressWarnings("unchecked")public static boolean verifySign(byte[] userId, byte[] publicKey, byte[] sourceData, byte[] signData) throws IOException{if (publicKey == null || publicKey.length == 0){return false;}if (sourceData == null || sourceData.length == 0){return false;}SM2 sm2 = SM2.Instance();ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);SM3Digest sm3 = new SM3Digest();byte[] z = sm2.sm2GetZ(userId, userKey);sm3.update(z, 0, z.length);sm3.update(sourceData, 0, sourceData.length);byte[] md = new byte[32];sm3.doFinal(md, 0);if (log.isDebugEnabled()){log.debug("SM3摘要值: " + Util.getHexString(md));}//System.out.println("SM3摘要值: " + Util.getHexString(md));//System.out.println();ByteArrayInputStream bis = new ByteArrayInputStream(signData);ASN1InputStream dis = new ASN1InputStream(bis);// 使用ASN1Object替换DERObject,ASN1Integer替换DERInteger// DERObject derObj = dis.readObject();// Enumeration e = ((ASN1Sequence) derObj).getObjects();ASN1Object derObj = (ASN1Object) dis.readObject();Enumeration e = ((ASN1Sequence) derObj).getObjects();BigInteger r = e.nextElement().getValue();BigInteger s = e.nextElement().getValue();SM2Result sm2Result = new SM2Result();sm2Result.r = r;sm2Result.s = s;//System.out.println("r: " + sm2Result.r.toString(16));//System.out.println("s: " + sm2Result.s.toString(16));//System.out.println("");sm2.sm2Verify(md, userKey, sm2Result.r, sm2Result.s, sm2Result);return sm2Result.r.equals(sm2Result.R);}}
5、版本区间1.64-1.75
(1)更新说明
2023-05-22 调整1.64-1.72区间代码,SM2和SM2Utils类,之前这个区间的代码贴成1.50-1.63区间的了,很抱歉。
2023-06-27 “版本1.64-1.72区间”调整为”版本1.64-1.75区间”,博主实测以下代码兼容1.73-1.75版本。
(2)引入依赖
我只演示区间内的其中一个版本,其他版本可自行替换测试,注意jdk版本。
org.bouncycastle bcprov-jdk15on 1.64
(3)关键代码
Cipher
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;import org.bouncycastle.crypto.params.ECPrivateKeyParameters;import org.bouncycastle.crypto.params.ECPublicKeyParameters;import org.bouncycastle.math.ec.ECPoint;import java.math.BigInteger;public class Cipher{ private int ct; private ECPoint p2; private SM3Digest sm3keybase; private SM3Digest sm3c3; private byte[] key; private byte keyOff; public Cipher() { this.ct = 1; this.key = new byte[32]; this.keyOff = 0; } private void Reset() { this.sm3keybase = new SM3Digest(); this.sm3c3 = new SM3Digest(); byte[] p = Util.byteConvert32Bytes(p2.normalize().getXCoord().toBigInteger()); this.sm3keybase.update(p, 0, p.length); this.sm3c3.update(p, 0, p.length); p = Util.byteConvert32Bytes(p2.normalize().getYCoord().toBigInteger()); this.sm3keybase.update(p, 0, p.length); this.ct = 1; NextKey(); } private void NextKey() { SM3Digest sm3keycur = new SM3Digest(this.sm3keybase); sm3keycur.update((byte) (ct >> 24 & 0xff)); sm3keycur.update((byte) (ct >> 16 & 0xff)); sm3keycur.update((byte) (ct >> 8 & 0xff)); sm3keycur.update((byte) (ct & 0xff)); sm3keycur.doFinal(key, 0); this.keyOff = 0; this.ct++; } public ECPoint Init_enc(SM2 sm2, ECPoint userKey) { AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair(); ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate(); ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic(); BigInteger k = ecpriv.getD(); ECPoint c1 = ecpub.getQ(); this.p2 = userKey.multiply(k); Reset(); return c1; } public void Encrypt(byte[] data) { this.sm3c3.update(data, 0, data.length); for (int i = 0; i < data.length; i++) { if (keyOff == key.length) { NextKey(); } data[i] ^= key[keyOff++]; } } public void Init_dec(BigInteger userD, ECPoint c1) { this.p2 = c1.multiply(userD); Reset(); } public void Decrypt(byte[] data) { for (int i = 0; i < data.length; i++) { if (keyOff == key.length) { NextKey(); } data[i] ^= key[keyOff++]; } this.sm3c3.update(data, 0, data.length); } public void Dofinal(byte[] c3) { byte[] p = Util.byteConvert32Bytes(p2.normalize().getYCoord().toBigInteger()); this.sm3c3.update(p, 0, p.length); this.sm3c3.doFinal(c3, 0); Reset(); }}
SM2
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;import org.bouncycastle.crypto.generators.ECKeyPairGenerator;import org.bouncycastle.crypto.params.ECDomainParameters;import org.bouncycastle.crypto.params.ECKeyGenerationParameters;import org.bouncycastle.crypto.params.ECPrivateKeyParameters;import org.bouncycastle.crypto.params.ECPublicKeyParameters;import org.bouncycastle.math.ec.ECCurve;import org.bouncycastle.math.ec.ECFieldElement;import org.bouncycastle.math.ec.ECFieldElement.Fp;import org.bouncycastle.math.ec.ECPoint;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.math.BigInteger;import java.security.SecureRandom;public class SM2{ //测试参数//public static final String[] ecc_param = {//"8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3",//"787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498",//"63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A",//"8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7",//"421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D",//"0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2"//};// 正式参数 为国密算法推荐参数public static String[] ecc_param = {"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF","FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC","28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93","FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123","32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7","BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"};public static SM2 Instance(){return new SM2();}public final BigInteger ecc_p;public final BigInteger ecc_a;public final BigInteger ecc_b;public final BigInteger ecc_n;public final BigInteger ecc_gx;public final BigInteger ecc_gy;public final ECCurve ecc_curve;public final ECPoint ecc_point_g;public final ECDomainParameters ecc_bc_spec;public final ECKeyPairGenerator ecc_key_pair_generator;public final ECFieldElement ecc_gx_fieldelement;public final ECFieldElement ecc_gy_fieldelement;public SM2(){this.ecc_p = new BigInteger(ecc_param[0], 16);this.ecc_a = new BigInteger(ecc_param[1], 16);this.ecc_b = new BigInteger(ecc_param[2], 16);this.ecc_n = new BigInteger(ecc_param[3], 16);this.ecc_gx = new BigInteger(ecc_param[4], 16);this.ecc_gy = new BigInteger(ecc_param[5], 16);this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);// 使用反射调用方法ECPoint ecc_point_g1 = null;try {// 此区间的bcprov版本的构造方法是私有的,无法直接new,只能通过反射来调用构造方法Class clazz = ECPoint.Fp.class;Constructor c1 = clazz.getDeclaredConstructor(new Class[]{ECCurve.class, ECFieldElement.class, ECFieldElement.class});c1.setAccessible(true);Class clazz2 = ECFieldElement.Fp.class;Constructor c2 = clazz2.getDeclaredConstructor(new Class[]{BigInteger.class, BigInteger.class, BigInteger.class});c2.setAccessible(true);//this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);// 此区间的bcprov版本的构造方法是私有的,无法直接new,只能通过反射来获取对象BigInteger r = calculateResidue(this.ecc_p);this.ecc_gx_fieldelement = (ECFieldElement.Fp)c2.newInstance(new Object[]{this.ecc_p, r, this.ecc_gx});// 此区间的bcprov版本的构造方法是私有的,无法直接new,只能通过反射来获取对象//this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);this.ecc_gy_fieldelement = (ECFieldElement.Fp)c2.newInstance(new Object[]{this.ecc_p, r, this.ecc_gy});this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);//this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);ecc_point_g1 = (ECPoint.Fp)c1.newInstance(new Object[]{this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement});} catch (NoSuchMethodException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}this.ecc_point_g = ecc_point_g1;this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);ECKeyGenerationParameters ecc_ecgenparam;ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());this.ecc_key_pair_generator = new ECKeyPairGenerator();this.ecc_key_pair_generator.init(ecc_ecgenparam);}public static BigInteger calculateResidue(BigInteger p){int bitLength = p.bitLength();if (bitLength >= 96){BigInteger firstWord = p.shiftRight(bitLength - 64);if (firstWord.longValue() == -1L){return ECConstants.ONE.shiftLeft(bitLength).subtract(p);}}return null;}public byte[] sm2GetZ(byte[] userId, ECPoint userKey){SM3Digest sm3 = new SM3Digest();int len = userId.length * 8;sm3.update((byte) (len >> 8 & 0xFF));sm3.update((byte) (len & 0xFF));sm3.update(userId, 0, userId.length);byte[] p = Util.byteConvert32Bytes(ecc_a);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(ecc_b);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(ecc_gx);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(ecc_gy);sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(userKey.normalize().getXCoord().toBigInteger());sm3.update(p, 0, p.length);p = Util.byteConvert32Bytes(userKey.normalize().getYCoord().toBigInteger());sm3.update(p, 0, p.length);byte[] md = new byte[sm3.getDigestSize()];sm3.doFinal(md, 0);return md;}public void sm2Sign(byte[] md, BigInteger userD, ECPoint userKey, SM2Result sm2Result){BigInteger e = new BigInteger(1, md);BigInteger k = null;ECPoint kp = null;BigInteger r = null;BigInteger s = null;do{do{// 正式环境AsymmetricCipherKeyPair keypair = ecc_key_pair_generator.generateKeyPair();ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) keypair.getPrivate();ECPublicKeyParameters ecpub = (ECPublicKeyParameters) keypair.getPublic();k = ecpriv.getD();kp = ecpub.getQ();// 国密规范测试 随机数k//String kS = "6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F";//k = new BigInteger(kS, 16);//kp = this.ecc_point_g.multiply(k);//System.out.println("计算曲线点X1: " + kp.getX().toBigInteger().toString(16));//System.out.println("计算曲线点Y1: " + kp.getY().toBigInteger().toString(16));//System.out.println("");// rr = e.add(kp.normalize().getXCoord().toBigInteger());r = r.mod(ecc_n);} while (r.equals(BigInteger.ZERO) || r.add(k).equals(ecc_n));// (1 + dA)~-1BigInteger da_1 = userD.add(BigInteger.ONE);da_1 = da_1.modInverse(ecc_n);// ss = r.multiply(userD);s = k.subtract(s).mod(ecc_n);s = da_1.multiply(s).mod(ecc_n);} while (s.equals(BigInteger.ZERO));sm2Result.r = r;sm2Result.s = s;}public void sm2Verify(byte[] md, ECPoint userKey, BigInteger r, BigInteger s, SM2Result sm2Result){sm2Result.R = null;BigInteger e = new BigInteger(1, md);BigInteger t = r.add(s).mod(ecc_n);if(t.equals(BigInteger.ZERO)){return;}else{ECPoint x1y1 = ecc_point_g.multiply(sm2Result.s);//System.out.println("计算曲线点X0: " + x1y1.getX().toBigInteger().toString(16));//System.out.println("计算曲线点Y0: " + x1y1.getY().toBigInteger().toString(16));//System.out.println("");x1y1 = x1y1.add(userKey.multiply(t));//System.out.println("计算曲线点X1: " + x1y1.getX().toBigInteger().toString(16));//System.out.println("计算曲线点Y1: " + x1y1.getY().toBigInteger().toString(16));//System.out.println("");sm2Result.R = e.add(x1y1.normalize().getXCoord().toBigInteger()).mod(ecc_n);//System.out.println("R: " + sm2Result.R.toString(16));return;}}}
SM2Utils
import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.bouncycastle.asn1.*;import org.bouncycastle.math.ec.ECPoint;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.math.BigInteger;import java.util.Enumeration;public class SM2Utils{private static Log log = LogFactory.getLog(SM2Utils.class);public static byte[] encrypt(byte[] publicKey, byte[] data) throws IOException{if (publicKey == null || publicKey.length == 0){return null;}if (data == null || data.length == 0){return null;}byte[] source = new byte[data.length];System.arraycopy(data, 0, source, 0, data.length);Cipher cipher = new Cipher();SM2 sm2 = SM2.Instance();ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);ECPoint c1 = cipher.Init_enc(sm2, userKey);cipher.Encrypt(source);byte[] c3 = new byte[32];cipher.Dofinal(c3);ASN1Integer x = new ASN1Integer(c1.normalize().getXCoord().toBigInteger());ASN1Integer y = new ASN1Integer(c1.normalize().getYCoord().toBigInteger());DEROctetString derDig = new DEROctetString(c3);DEROctetString derEnc = new DEROctetString(source);ASN1EncodableVector v = new ASN1EncodableVector();v.add(x);v.add(y);v.add(derDig);v.add(derEnc);DERSequence seq = new DERSequence(v);ByteArrayOutputStream bos = new ByteArrayOutputStream();try {// 此区间的bcprov版本的构造方法是私有的,无法直接new,只能通过反射来调用构造方法Class clazz = ASN1OutputStream.class;Constructor c4 = clazz.getDeclaredConstructor(new Class[]{OutputStream.class});c4.setAccessible(true);//ASN1OutputStream dos = new ASN1OutputStream(bos);ASN1OutputStream dos = (ASN1OutputStream) c4.newInstance(new Object[]{bos});dos.writeObject(seq);return bos.toByteArray();} catch (Exception e) {e.printStackTrace();}return null;}public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException{if (privateKey == null || privateKey.length == 0){return null;}if (encryptedData == null || encryptedData.length == 0){return null;}byte[] enc = new byte[encryptedData.length];System.arraycopy(encryptedData, 0, enc, 0, encryptedData.length);SM2 sm2 = SM2.Instance();BigInteger userD = new BigInteger(1, privateKey);ByteArrayInputStream bis = new ByteArrayInputStream(enc);ASN1InputStream dis = new ASN1InputStream(bis);// 使用ASN1Object替换DERObject,ASN1Integer替换DERInteger// DERObject derObj = dis.readObject();// ASN1Sequence asn1 = (ASN1Sequence) derObj;// DERInteger x = (DERInteger) asn1.getObjectAt(0);// DERInteger y = (DERInteger) asn1.getObjectAt(1);ASN1Object derObj = (ASN1Object) dis.readObject();ASN1Sequence asn1 = (ASN1Sequence) derObj;ASN1Integer x = (ASN1Integer) asn1.getObjectAt(0);ASN1Integer y = (ASN1Integer) asn1.getObjectAt(1);ECPoint c1 = sm2.ecc_curve.createPoint(x.getValue(), y.getValue());Cipher cipher = new Cipher();cipher.Init_dec(userD, c1);DEROctetString data = (DEROctetString) asn1.getObjectAt(3);enc = data.getOctets();cipher.Decrypt(enc);byte[] c3 = new byte[32];cipher.Dofinal(c3);return enc;}public static byte[] sign(byte[] userId, byte[] privateKey, byte[] sourceData) throws IOException{if (privateKey == null || privateKey.length == 0){return null;}if (sourceData == null || sourceData.length == 0){return null;}SM2 sm2 = SM2.Instance();BigInteger userD = new BigInteger(privateKey);//System.out.println("userD: " + userD.toString(16));//System.out.println("");ECPoint userKey = sm2.ecc_point_g.multiply(userD);//System.out.println("椭圆曲线点X: " + userKey.getX().toBigInteger().toString(16));//System.out.println("椭圆曲线点Y: " + userKey.getY().toBigInteger().toString(16));//System.out.println("");SM3Digest sm3 = new SM3Digest();byte[] z = sm2.sm2GetZ(userId, userKey);//System.out.println("SM3摘要Z: " + Util.getHexString(z));//System.out.println("");////System.out.println("M: " + Util.getHexString(sourceData));//System.out.println("");sm3.update(z, 0, z.length);sm3.update(sourceData, 0, sourceData.length);byte[] md = new byte[32];sm3.doFinal(md, 0);//System.out.println("SM3摘要值: " + Util.getHexString(md));//System.out.println("");SM2Result sm2Result = new SM2Result();sm2.sm2Sign(md, userD, userKey, sm2Result);//System.out.println("r: " + sm2Result.r.toString(16));//System.out.println("s: " + sm2Result.s.toString(16));//System.out.println("");ASN1Integer d_r = new ASN1Integer(sm2Result.r);ASN1Integer d_s = new ASN1Integer(sm2Result.s);//BigInteger d_r = sm2Result.r;//BigInteger d_s = sm2Result.s;ASN1EncodableVector v2 = new ASN1EncodableVector();v2.add(d_r);v2.add(d_s);// 调整DER编码的写法// DERObject sign = new DERSequence(v2);// byte[] signdata = sign.getDEREncoded();ASN1Object sign = new DERSequence(v2);byte[] signdata = sign.getEncoded("DER");return signdata;}@SuppressWarnings("unchecked")public static boolean verifySign(byte[] userId, byte[] publicKey, byte[] sourceData, byte[] signData) throws IOException{if (publicKey == null || publicKey.length == 0){return false;}if (sourceData == null || sourceData.length == 0){return false;}SM2 sm2 = SM2.Instance();ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);SM3Digest sm3 = new SM3Digest();byte[] z = sm2.sm2GetZ(userId, userKey);sm3.update(z, 0, z.length);sm3.update(sourceData, 0, sourceData.length);byte[] md = new byte[32];sm3.doFinal(md, 0);if (log.isDebugEnabled()){log.debug("SM3摘要值: " + Util.getHexString(md));}//System.out.println("SM3摘要值: " + Util.getHexString(md));//System.out.println();ByteArrayInputStream bis = new ByteArrayInputStream(signData);ASN1InputStream dis = new ASN1InputStream(bis);// 使用ASN1Object替换DERObject,ASN1Integer替换DERInteger// DERObject derObj = dis.readObject();// Enumeration e = ((ASN1Sequence) derObj).getObjects();ASN1Object derObj = (ASN1Object) dis.readObject();Enumeration e = ((ASN1Sequence) derObj).getObjects();BigInteger r = e.nextElement().getValue();BigInteger s = e.nextElement().getValue();SM2Result sm2Result = new SM2Result();sm2Result.r = r;sm2Result.s = s;//System.out.println("r: " + sm2Result.r.toString(16));//System.out.println("s: " + sm2Result.s.toString(16));//System.out.println("");sm2.sm2Verify(md, userKey, sm2Result.r, sm2Result.s, sm2Result);return sm2Result.r.equals(sm2Result.R);}}
虽然贴了很多代码,但是如果用对比工具看的话,实际改动的代码也就二三十行而已,主要是高版本的方法稍微改了,还有个别方法无法直接使用,需要用反射来调用,仅此而已。
当然,除了上述的代码调整外,还可以使用第三方工具包,比如hutool,不过也是需要注意hutool也有适用的bcprov版本;还有方法是隔离jar包加载,让特定的类加载特定版本的jar包版本,这两种方式如果想看的也可以评论区提出。
感谢各位大佬的阅读,还望动动小手,一键三连就行了,给博主一点创作的动力。
来源地址:https://blog.csdn.net/hahawangzi520/article/details/127971967