关于ECDHE_SM4_CBC_SM3的实现
tommy8421 opened this issue · comments
0、ECDHE交换算法测试过的主机:https://e.tf.cn(须指定ECDHE_SM4_CBC_SM3否则服务器协商优先ECC_SM4_CBC_SM3)和https://demo.gmssl.cn:1443
1、在ServerKeyExchange阶段,按下面描述保存服务器公钥(ephemeral):
// 03 -- ECCurveType-->named_curve(3) -- 目前只有这个合法值,其他要么废弃要么是RFU的了
// 00 17 -- secp256r1 (23) sm2p256v1(41) -- 按TLCP规范,这里是不用校验的
// 41 -- length
// 04 -- SM2 public key(point = x + y)
// 8fc16899e15b5110b1b3cf45a331bd8b9c25bf2afb0f0b31faa1e7106a7f36bae75bba7938ddacbe5868fe4e64755db631199c95802c550e4342dbcbd54b937a
// 00 47 -- 下面为ASN.1签名数据signed_param(用服务器签名公钥验签,验签数据:client random+server random + ECDHE parameter)
// 30 45
// 02 20 3ed134bc05dbc842a31c83bf6b5c89ba2c504257736ab436d6e65ecbcac53dc8 -R
// 02 21 00b2541eb483f7faaa5f22e0502049104944d8a423de69a2eac757d1c6cfca5e3f -S
2、在ClientKeyExchange阶段,计算PreMasterKey,借助于 BC库的 SM2KeyExchange类;然后把Ephemeral 的PublicKey发给服务器
PrivateKey localEncPVK = session.getKeyManager().getPrivateKey(GMConstants.CERT_ENC);
KeyPair localEphKP = Crypto.generateSM2KeyPair(); // Ephemeral key pair
PublicKey peerEphPUK = Crypto.loadPublicKey(session.getServerKEParam()); // 这里就是之前SKE的时候服务器的ECPoint为PublicKey
byte[] plainSecret = Crypto.generateECDHESecret(localEncPVK, localEphKP.getPrivate(), peerEncPUK, peerEphPUK);
核心代码如下:
public static KeyPair generateSM2KeyPair()
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyPairGenerator generator = KeyPairGenerator.getInstance(“EC”, GMProvider.BC_PROVIDER); // new BouncyCastleProvider()
generator.initialize(new ECGenParameterSpec(GM_NAME)); // GM_NAME="sm2p256v1"
return generator.generateKeyPair();
}
public static PublicKey loadPublicKey(byte[] pointBytes)
throws InvalidKeySpecException, NoSuchAlgorithmException {
ECParameterSpec ecParamSpec = org.bouncycastle.jce.ECNamedCurveTable.getParameterSpec(GM_NAME);
ECPoint ecPoint = ecParamSpec.getCurve().decodePoint(pointBytes);
ECPublicKeySpec ecPUKSpec = new ECPublicKeySpec(ecPoint, ecParamSpec);
KeyFactory keyFactory = KeyFactory.getInstance("EC", GMProvider.BC_PROVIDER);
return keyFactory.generatePublic(ecPUKSpec);
}
public static byte[] generateECDHESecret(PrivateKey localEncPVK, PrivateKey localEphPVK, PublicKey peerEncPUK, PublicKey peerEphPUK) {
CipherParameters sm2kePvkParam = new SM2KeyExchangePrivateParameters(
false, // for client
((BCECPrivateKey) localEncPVK).engineGetKeyParameters(),
((BCECPrivateKey) localEphPVK).engineGetKeyParameters());
CipherParameters pvkParamWithID = new ParametersWithID(sm2kePvkParam, GM_USERID);
// 因为 BCECPublicKey.engineGetKeyParameters 没有公开,也可以用反射调用
ECPublicKeyParameters peerPUKParam = new ECPublicKeyParameters(((BCECPublicKey) peerEncPUK).getQ(), ecDomainParameters);
ECPublicKeyParameters peerEphPUKParam = new ECPublicKeyParameters(((BCECPublicKey) peerEphPUK).getQ(), ecDomainParameters);
CipherParameters sm2kePukParam = new SM2KeyExchangePublicParameters(peerPUKParam, peerEphPUKParam);
CipherParameters pukParamWithID = new ParametersWithID(sm2kePukParam, GM_USERID);// GM_USERID="1234567812345678".getBytes()
SM2KeyExchange keyExchange = new SM2KeyExchange();
keyExchange.init(pvkParamWithID);
return keyExchange.calculateKey(GMConstants.ECDHE_KEY_BITS, pukParamWithID); // pre-master key bits number, ECDHE_KEY_BITS=48*8
}
可以看我的仓库,AndroidGMSSE。实现了E011/E051/E013/E053.