考虑到网上大多数SM2密钥交换协议均是C语言实现,没有找到java实现的版本,所以参考了网上SM2加密算法实现的代码,自行写出了Java版本的密钥交换算法。
本文中的SM2.java SM3.java SM3Digest.java Util.java来源于CSDN博客:https://blog.csdn.net/ErErFei/article/details/50998162
本文程序均在命令行窗口运行通过。由于密钥对不断随机产生,所以运行结果和本文是不相同的。
SM2.java文件
import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.ECFieldElement.Fp;
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;//素数p
public final BigInteger ecc_a;//系数a
public final BigInteger ecc_b;//系数b
public final BigInteger ecc_n;//生成元G的阶n
public final BigInteger ecc_gx; //生成元G是椭圆曲线的一个点,该点的横坐标x
public final BigInteger ecc_gy; //G的纵坐标
public final ECCurve ecc_curve; //椭圆曲线
public final ECPoint ecc_point_g; //点G
public final int ecc_w;
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_w = 127;
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);
}
}
SM2_Exchange.java
import java.math.BigInteger;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import ren_sm3.Util;
import ren_sm3.SM3Digest;
public class SM2_Exchange
{
private byte[] pubKey;
private byte[] priKey;
//生成密钥对
public void generateKeyPair(){
SM2 sm2 = SM2.Instance(); //生成SM2实例
AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair(); //生成公私密钥对
ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate(); //从密钥对中提取私钥参数
ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic(); //从密钥对中提取公钥参数
BigInteger privateKey = ecpriv.getD(); //私钥是个大整数
ECPoint publicKey = ecpub.getQ(); //公钥是个点
pubKey = publicKey.getEncoded(); //将椭圆曲线点转化为字节数组
priKey = privateKey.toByteArray(); //将大整数转化为字节数组
System.out.println(Util.getHexString(priKey));
}
//获取公钥
public byte[] getPubKey()
{
return pubKey;
}
//获取私钥
public byte[] getPriKey()
{
return priKey;
}
/**
* 计算Z值
* @param ID 身份标识
* @param Z 存储32字节杂凑值的字节数组
*/
public void computeZ(String ID,byte[] Z)throws Exception
{
SM3Digest sm3=new SM3Digest();
byte[] ENTL = new byte[]{0x00,(byte)0x80};
byte[] byte_ID = ID.getBytes("US-ASCII");//将字符串以ASCII格式编码为字节数组
sm3.update(ENTL,0,ENTL.length);
sm3.update(byte_ID,0,byte_ID.length);
SM2 sm2 = SM2.Instance();
byte[] a = Util.hexToByte(sm2.ecc_a.toString(16));//将椭圆曲线的参数a大整数格式转化为字节数组
byte[] b = Util.hexToByte(sm2.ecc_b.toString(16));//将椭圆曲线的参数b大整数格式转化为字节数组
sm3.update(a,0,a.length);
sm3.update(b,0,b.length);
byte[] gx = Util.hexToByte(sm2.ecc_gx.toString(16));//将椭圆曲线的参数gx大整数格式转化为字节数组
byte[] gy = Util.hexToByte(sm2.ecc_gy.toString(16));//将椭圆曲线的参数gy大整数格式转化为字节数组
sm3.update(gx,0,gx.length);
sm3.update(gy,0,gy.length);
sm3.update(pubKey,1,pubKey.length-1);//将公钥的横坐标和纵坐标导入缓冲区
sm3.doFinal(Z,0);
}
public static void main(String[] args)throws Exception
{
SM2_Exchange A = new SM2_Exchange();
SM2_Exchange B = new SM2_Exchange();
A.generateKeyPair();//产生A的公私钥对
B.generateKeyPair();//产生B的公私钥对
byte[] pubK_A = A.getPubKey();//获取A的公钥
byte[] pubK_B = B.getPubKey();//获取B的公钥
byte[] priK_A = A.getPriKey();//获取A的私钥
byte[] priK_B = B.getPriKey();//获取B的私钥
byte[] ZA =new byte[32];
A.computeZ("288976@qq.com",ZA);//根据输入的标识计算ZA值
byte [] ZB =new byte[32];
B.computeZ("288975@qq.com",ZB);//根据输入的标识计算ZB值
Exch A_EX = new Exch();
Exch B_EX = new Exch();
A_EX.Init(priK_A);//产生rA,RA,x_1,tA
B_EX.Init(priK_B);//产生rB,RB,x_2,tB
ECPoint R_A = A_EX.R1;//获取A产生的RA
ECPoint R_B = B_EX.R1;//获取B产生的RB
SM2 sm2 = new SM2();
ECPoint pubA = sm2.ecc_curve.decodePoint(pubK_A);//将A公钥的字节数组格式转化为椭圆曲线点
ECPoint pubB = sm2.ecc_curve.decodePoint(pubK_B);//将B公钥的字节数组格式转化为椭圆曲线点
A_EX.computeKey(pubB,R_B,ZA,ZB);//A计算x_2,UA,KA
B_EX.computeKey(pubA,R_A,ZA,ZB);//B计算X_1,UB,KB
}
}
Exch.java
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import java.math.BigInteger;
import ren_sm3.SM3Digest;
import ren_sm3.Util;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECFieldElement.Fp;
public class Exch
{
private BigInteger t;
public ECPoint R1;
private byte[] ux;//椭圆曲线点U的横坐标
p