文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

java bcprov 国密 依赖 jar包 版本 升级 降级 教程

2023-08-17 22:04

关注

关于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-Idartifact-Idversion完整jar包名
org.bouncycastlebcprov-jdk151.32bcprov-jdk15-1.32.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15
bcprov-jdk16
1.38bcprov-jdk14-1.38.jar
bcprov-jdk15-1.38.jar
bcprov-jdk16-1.38.jar
org.bouncycastlebcprov-jdk15
bcprov-jdk16
1.40bcprov-jdk15-1.40.jar
bcprov-jdk16-1.40.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15
bcprov-jdk16
1.43bcprov-jdk14-1.43.jar
bcprov-jdk15-1.43.jar
bcprov-jdk16-1.43.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15
bcprov-jdk16
1.44bcprov-jdk14-1.44.jar
bcprov-jdk15-1.44.jar
bcprov-jdk16-1.44.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15
bcprov-jdk16
1.44bcprov-jdk14-1.44.jar
bcprov-jdk15-1.44.jar
bcprov-jdk16-1.44.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15
bcprov-jdk16
1.45bcprov-jdk14-1.45.jar
bcprov-jdk15-1.45.jar
bcprov-jdk16-1.45.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15
bcprov-jdk15+
bcprov-jdk15on
bcprov-jdk16
1.46bcprov-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.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.47bcprov-jdk14-1.47.jar
bcprov-jdk15on-1.47.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.48bcprov-jdk14-1.48.jar
bcprov-jdk15on-1.48.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.49bcprov-jdk14-1.49.jar
bcprov-jdk15on-1.49.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.50bcprov-jdk14-1.50.jar
bcprov-jdk15on-1.50.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.51bcprov-jdk14-1.51.jar
bcprov-jdk15on-1.51.jar
org.bouncycastlebcprov-jdk15on1.52bcprov-jdk15on-1.52.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.53bcprov-jdk14-1.53.jar
bcprov-jdk15on-1.53.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.54bcprov-jdk14-1.54.jar
bcprov-jdk15on-1.54.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.55bcprov-jdk14-1.55.jar
bcprov-jdk15on-1.55.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.56bcprov-jdk14-1.56.jar
bcprov-jdk15on-1.56.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.57bcprov-jdk14-1.57.jar
bcprov-jdk15on-1.57.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.58bcprov-jdk14-1.58.jar
bcprov-jdk15on-1.58.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.59bcprov-jdk14-1.59.jar
bcprov-jdk15on-1.59.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.60bcprov-jdk14-1.60.jar
bcprov-jdk15on-1.60.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.61bcprov-jdk14-1.61.jar
bcprov-jdk15on-1.61.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
1.62bcprov-jdk14-1.62.jar
bcprov-jdk15on-1.62.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
bcprov-jdk15to18
1.63bcprov-jdk14-1.63.jar
bcprov-jdk15on-1.63.jar
bcprov-jdk15to18-1.63.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
bcprov-jdk15to18
1.64bcprov-jdk14-1.64.jar
bcprov-jdk15on-1.64.jar
bcprov-jdk15to18-1.64.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
bcprov-jdk15to18
1.65bcprov-jdk14-1.65.jar
bcprov-jdk15on-1.65.jar
bcprov-jdk15to18-1.65.jar
org.bouncycastlebcprov-jdk15on
bcprov-jdk15to18
1.66bcprov-jdk15-1.66.jar
bcprov-jdk16-1.66.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
bcprov-jdk15to18
1.67bcprov-jdk14-1.67.jar
bcprov-jdk15on-1.67.jar
bcprov-jdk15to18-1.67.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
bcprov-jdk15to18
1.68bcprov-jdk14-1.68.jar
bcprov-jdk15on-1.68.jar
bcprov-jdk15to18-1.68.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
bcprov-jdk15to18
1.69bcprov-jdk14-1.69.jar
bcprov-jdk15on-1.69.jar
bcprov-jdk15to18-1.69.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15on
bcprov-jdk15to18
1.70bcprov-jdk14-1.70.jar
bcprov-jdk15on-1.70.jar
bcprov-jdk15to18-1.70.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15to18
bcprov-jdk18on
1.71bcprov-jdk14-1.71.jar
bcprov-jdk15to18-1.71.jar
bcprov-jdk18on-1.71.jar
org.bouncycastlebcprov-jdk14
bcprov-jdk15to18
bcprov-jdk18on
1.72bcprov-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-Idartifact-Idversion完整jar包名
org.bouncycastlebcprov-jdk14
bcprov-jdk15to18
bcprov-jdk18on
1.73bcprov-jdk14-1.73.jar
bcprov-jdk15to18-1.73.jar
bcprov-jdk18on-1.73.jar
org.bouncycastlebcprov-jdk15to18
bcprov-jdk18on
1.74bcprov-jdk15to18-1.74.jar
bcprov-jdk18on-1.74.jar
org.bouncycastlebcprov-jdk15to18
bcprov-jdk18on
1.75bcprov-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

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯