使用SM2算法进行密钥交换的代码(BC包装)
SM2是国家密码管理局于2010年12月17日发布的椭圆曲线公钥密码算法。SM2算法和RSA算法都是公钥密码算法,SM2算法是一种更先进安全的算法,在我们国家商用密码体系中被用来替换RSA算法。
SM2密钥交换的优点
- 密钥更短 ;
- 速度更快 ;
- 更加安全;
说明如下
BouncyCastleProvider bcp = new BouncyCastleProvider();
Security.addProvider(bcp);
需要在工程里面引用BC库。到http://www.bouncycastle.org/ 下载bcprov-ext-jdk15on-165.jar
在eclipse亲测可用。
把下面的两个文件完全复制下来,文件名已经包含在文件头。
存成两个java文件。
直接就可以执行了。
用SM2 做密钥交换虽然可以,但经过这个测试后,本人感觉SM2密钥交换比DH麻烦太多了!商用环境还是需要提高便利性,让大多数程序员去了解算法的椭圆曲线细节不太现实。
如果有致力于商密算法研究,特别是BC优化方面的人, 建议对此进行优化,把算法的细节尽量全部包装在库中。
代码如下:
/***
* xuyanbai 2020.6.6 文件名 SM2Coder_Ag.java
*/
package xu.edu.testSM2_Agree;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.agreement.SM2KeyExchange;
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.crypto.params.ParametersWithID;
import org.bouncycastle.crypto.params.SM2KeyExchangePrivateParameters;
import org.bouncycastle.crypto.params.SM2KeyExchangePublicParameters;
import org.bouncycastle.math.ec.ECConstants;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.test.TestRandomBigInteger;
public class SM2Coder_Ag {
//xuyanbai add it for curve use
//come from GB T 32918.3-2016 and BC
static BigInteger SM2_ECC_P = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3", 16); //素数域
static BigInteger SM2_ECC_A = new BigInteger("787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 16); //曲线系数a
static BigInteger SM2_ECC_B = new BigInteger("63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 16); //曲线系数b
static BigInteger SM2_ECC_N = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 16); //生成元G的阶数
static BigInteger SM2_ECC_H = ECConstants.ONE; //余因子为1
static BigInteger SM2_ECC_GX = new BigInteger("421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 16); //生成元x坐标
static BigInteger SM2_ECC_GY = new BigInteger("0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2", 16); 生成元x坐标
//获取椭圆曲线
private static ECDomainParameters domainParams=null;
private static void getcurve()
{
if(domainParams !=null)return;
ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B, SM2_ECC_N, SM2_ECC_H);
ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
return ;
}
/**
* 初始化甲方密钥
*
* @return Map 甲方密钥Map
* @throws Exception
*/
public static Map<String, Object> initKeyA() throws Exception {
// 构造曲线
getcurve();
// 实例化密钥对生成器
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
//A用户私钥6FCBA2EF9AE0AB902BC3BDE3FF915D44BA4CC78F88E2F8E7F8996D3B8CCEEDEE
ECKeyGenerationParameters aKeyGenParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("6FCBA2EF9AE0AB902BC3BDE3FF915D44BA4CC78F88E2F8E7F8996D3B8CCEEDEE", 16));
// 初始化密钥对生成器
keyPairGenerator.init(aKeyGenParams);
// 生成密钥对
// KeyPair keyPair = keyPairGenerator.generateKeyPair();
AsymmetricCipherKeyPair aKp = keyPairGenerator.generateKeyPair();
// 甲方公钥
//PublicKey publicKey = (PublicKey) keyPair.getPublic();
ECPublicKeyParameters aPub = (ECPublicKeyParameters)aKp.getPublic();
// 甲方私钥
//PrivateKey privateKey = (PrivateKey) keyPair.getPrivate();
ECPrivateKeyParameters aPriv = (ECPrivateKeyParameters)aKp.getPrivate();
//ra随机数
//ECKeyGenerationParameters aeKeyGenParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("83A2C9C8B96E5AF70BD480B472409A9A327257F1EBB73F5B073354B248668563", 16));
SecureRandom random= new SecureRandom();
ECKeyGenerationParameters aeKeyGenParams = new ECKeyGenerationParameters(domainParams, random);
keyPairGenerator.init(aeKeyGenParams);
AsymmetricCipherKeyPair aeKp = keyPairGenerator.generateKeyPair();
ECPublicKeyParameters aePub = (ECPublicKeyParameters)aeKp.getPublic();
ECPrivateKeyParameters aePriv = (ECPrivateKeyParameters)aeKp.getPrivate();
// 将密钥对存储在Map中
Map<String, Object> keyMap = new HashMap<String, Object>(4);
//keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put("aPub", aPub);
//keyMap.put(PRIVATE_KEY, privateKey);
keyMap.put("aPriv", aPriv);
keyMap.put("aePub",aePub);
keyMap.put("aePriv",aePriv);
return keyMap;
}
/**
* 初始化乙方密钥
*
* @param key
* 甲方公钥
* @return Map 乙方密钥Map
* @throws Exception
*/
public static Map<String, Object> initKeyB() throws Exception {
//rb ,乙方
ECKeyGenerationParameters bKeyGenParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("5E35D7D3F3C54DBAC72E61819E730B019A84208CA3A35E4C2E353DFCCB2A3B53", 16));
// 初始化算法参数生成器
// 实例化密钥对儿生成器
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
// 初始化密钥对儿生成器
keyPairGenerator.init(bKeyGenParams);
// 生成密钥对儿
AsymmetricCipherKeyPair bKp = keyPairGenerator.generateKeyPair();
//获取B用户的公私钥
ECPublicKeyParameters bPub = (ECPublicKeyParameters)bKp.getPublic();
ECPrivateKeyParameters bPriv = (ECPrivateKeyParameters)bKp.getPrivate();
//获取be的参数,rb随机
SecureRandom random= new SecureRandom();
//ECKeyGenerationParameters beKeyGenParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("33FE21940342161C55619C4A0C060293D543C80AF19748CE176D83477DE71C80", 16));
ECKeyGenerationParameters beKeyGenParams = new ECKeyGenerationParameters(domainParams, random);
keyPairGenerator.init(beKeyGenParams);
AsymmetricCipherKeyPair beKp = keyPairGenerator.generateKeyPair();
ECPublicKeyParameters bePub = (ECPublicKeyParameters)beKp.getPublic();
ECPrivateKeyParameters bePriv = (ECPrivateKeyParameters)beKp.getPrivate();
// 封装密钥
Map<String, Object> map = new HashMap<String, Object>(4);
//map.put(PUBLIC_KEY, publicKey);
map.put("bPub", bPub);
//map.put(PRIVATE_KEY, privateKey);
map.put("bPriv", bPriv);
map.put("bePub", bePub);
map.put("bePriv", bePriv);
return map;
}
/***
*
* @param aPriv
* @param aePriv
* @param bPub
* @param bePub
* @return 协商后的密钥
* @throws Exception
*/
public static byte[] KeyExchangeA(ECPrivateKeyParameters aPriv,ECPrivateKeyParameters aePriv,ECPublicKeyParameters bPub,ECPublicKeyParameters bePub) throws Exception {
SM2KeyExchange exch = new SM2KeyExchange();
exch.init(new ParametersWithID(new SM2KeyExchangePrivateParameters(true, aPriv, aePriv), Strings.toByteArray("ALICE123@YAHOO.COM")));
byte[] k1 = exch.calculateKey(128, new ParametersWithID(new SM2KeyExchangePublicParameters(bPub, bePub), Strings.toByteArray("BILL456@YAHOO.COM")));
//共享密钥是55b0ac62a6b927ba23703832c853ded4
// isTrue("key 1 wrong", Arrays.areEqual(Hex.decode("55b0ac62a6b927ba23703832c853ded4"), k1));
return k1;
}
/****
*
* @param bPriv
* @param bePriv
* @param aPub
* @param aePub
* @return 返回协商后的密钥
* @throws Exception
*/
public static byte[] KeyExchangeB(ECPrivateKeyParameters bPriv,ECPrivateKeyParameters bePriv,ECPublicKeyParameters aPub,ECPublicKeyParameters aePub) throws Exception {
SM2KeyExchange exch = new SM2KeyExchange();
exch.init(new ParametersWithID(new SM2KeyExchangePrivateParameters(false, bPriv, bePriv), Strings.toByteArray("BILL456@YAHOO.COM")));
byte[] k2 = exch.calculateKey(128, new ParametersWithID(new SM2KeyExchangePublicParameters(aPub, aePub), Strings.toByteArray("ALICE123@YAHOO.COM")));
// isTrue("key 2 wrong", Arrays.areEqual(Hex.decode("55b0ac62a6b927ba23703832c853ded4"), k2));
return k2;
}
/**
* 取得私钥
*
* @param keyMap
* 密钥Map
* @return byte[] 私钥
* @throws Exception
*/
public static ECPrivateKeyParameters getPrivateKey(String str,Map<String, Object> keyMap )
throws Exception {
ECPrivateKeyParameters key = (ECPrivateKeyParameters) keyMap.get(str);
return key;
}
/**
* 取得公钥
*
* @param keyMap
* 密钥Map
* @return byte[] 公钥
* @throws Exception
*/
public static ECPublicKeyParameters getPublicKey(String str,Map<String, Object> keyMap )
throws Exception {
ECPublicKeyParameters key = (ECPublicKeyParameters) keyMap.get(str);
return key;
}
}
下面的文件是用前面的包装类进行测试 的文件:
/***
* xuyanbai 2020.6.6 文件名:SM2Coder_AgTest.java
*/
package xu.edu.testSM2_Agree;
import java.security.Security;
import java.util.Map;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class SM2Coder_AgTest {
public final void test() throws Exception {
// 生成甲方密钥对儿
Map<String, Object> keyMap1 = SM2Coder_Ag.initKeyA();
// 生成乙方密钥对儿
Map<String, Object> keyMap2 = SM2Coder_Ag.initKeyB();
//甲方生成的密钥
ECPrivateKeyParameters aPriv = SM2Coder_Ag.getPrivateKey("aPriv", keyMap1);
ECPrivateKeyParameters aePriv = SM2Coder_Ag.getPrivateKey("aePriv", keyMap1);
ECPublicKeyParameters aPub = SM2Coder_Ag.getPublicKey("aPub", keyMap1);
ECPublicKeyParameters aePub = SM2Coder_Ag.getPublicKey("aePub", keyMap1);
//乙方生成的密钥
ECPrivateKeyParameters bPriv = SM2Coder_Ag.getPrivateKey("bPriv", keyMap2);
ECPrivateKeyParameters bePriv = SM2Coder_Ag.getPrivateKey("bePriv", keyMap2);
ECPublicKeyParameters bPub = SM2Coder_Ag.getPublicKey("bPub", keyMap2);
ECPublicKeyParameters bePub = SM2Coder_Ag.getPublicKey("bePub", keyMap2);
//开始协商
byte[] mykeyA = SM2Coder_Ag.KeyExchangeA(aPriv, aePriv, bPub, bePub);
byte[] mykeyB = SM2Coder_Ag.KeyExchangeB(bPriv, bePriv, aPub, aePub);
//输出结果
System.out.println("甲方协商结果:\n" + Base64.toBase64String(mykeyA));
System.out.println("乙方协商结果:\n" + Base64.toBase64String(mykeyB));
}
public static void main(String[] args) throws Exception {
//初始化BC库
BouncyCastleProvider bcp = new BouncyCastleProvider();
Security.addProvider(bcp);
SM2Coder_AgTest a =new SM2Coder_AgTest();
a.test();
}
}
执行结果类似如下:(每次协商均为不同的密钥值)
甲方协商结果:
ywR8rVvf8dqKt/B2ovPlXw==
乙方协商结果:
ywR8rVvf8dqKt/B2ovPlXw==