SM9算法C++实现系列目录:
基于JPBC的SM9算法的java实现与测试
国密SM9算法C++实现之0:源码下载地址
国密SM9算法C++实现之一:算法简介
国密SM9算法C++实现之二:测试工具
国密SM9算法C++实现之三:椭圆曲线接口、参数初始化
国密SM9算法C++实现之四:基本功能函数与KGC接口的实现
国密SM9算法C++实现之五:签名验签算法
国密SM9算法C++实现之六:密钥封装解封算法
国密SM9算法C++实现之七:加密解密算法
国密SM9算法C++实现之八:密钥交换算法
国密SM9算法C++实现之九:算法功能与测试例子
SM9密钥交换结果包括两个可选的哈希验证值和一个共享密钥值,,对此,也简单将其封装为一个类。
根据算法描述和算法流程图,可以看到,在密钥交换的第1步~第3部中,需要产生一个[1,N-1]范围内的随机数,并计算G1群上的倍点,可以个过程理解为生成G1群上的一对公私钥,私钥就是随机数,公钥就是倍点,点是通过主公钥和G1的几点P1运算后得到的,倍数就是私钥。
同时,在A4和B7中,需要将公钥发送给对方。因此,此处将前3步提取出来,作为密钥交换的初始化部分,这个接口命名为keyExchange_init。
keyExchange_init密钥协商初始化的结果是产生了一对G1上的临时公私钥,将其封装为TempKeyPair,并放在密钥交换结果值之中。
#ifndef YY_SM9_KEYAGREEMENT_INCLUDE_H__
#define YY_SM9_KEYAGREEMENT_INCLUDE_H__
#pragma once
#include
using namespace std;
/**
* 密钥交换结果值类,包括两个hash验证值和共享密钥.
* @author YaoYuan
*/
class KeyAgreement {
public:
KeyAgreement() {}
KeyAgreement(const string& shareKey, const string& hashA2, const string& hashB1) {
mShareKey = shareKey;
mHashA2 = hashA2;
mHashB1 = hashB1;
}
~KeyAgreement() {}
public:
string getShareKey() {
return mShareKey;
}
string getHashA2() {
return mHashA2;
}
string getHashB1() {
return mHashB1;
}
private:
/** 共享密钥. */
string mShareKey;
/** 选项S_A或者S_2. */
string mHashA2;
/** 选项S_B或者S_1. */
string mHashB1;
};
/**
* 密钥交换中的临时密钥对,是群G1上的一对公私钥.
* @author YaoYuan
*/
class TempKeyPair {
public:
TempKeyPair() {};
TempKeyPair(const string& prikey, const string& pubkey) : mPrikey(prikey), mPubkey(pubkey) { }
public:
string getPrivate() const { return mPrikey; }
string getPublic() const { return mPubkey; }
private:
string mPrikey;
string mPubkey;
};
#endif
根据上面的描述,定义两个接口函数:
/**
* 密钥交换步骤1:初始化
*
* @param masterPublicKey 加密主公钥
* @param othId 对方ID
* @return 临时密钥对
* @throw std::exception SM9_ERROR_NOT_INIT
*/
static TempKeyPair keyExchange_init(const string& masterPublicKey, const string& othId);
/**
* 密钥交换步骤2:计算共享密钥
*
* @param masterPublicKey 加密主公钥
* @param isSponsor true-发起方;false-响应方
* @param myId 己方ID
* @param othId 对方ID
* @param myTempKeyPair 己方临时密钥对
* @param othTempPubkey 对方临时公钥
* @param klen 要计算的共享密钥字节长度
* @return 密钥交换值
* @throw std::exception SM9_ERROR_NOT_INIT | SM9_ERROR_CALC_RATE | SM9_ERROR_KEYEXCHANGE_R_NOT_ON_G1
*/
static KeyAgreement keyExchange(const string& masterPublicKey, bool isSponsor, const string& myId, const string& othId,
const string& myPrikey, const TempKeyPair& myTempKeyPair, const string& othTempPubkey, int klen);
需要注意的是,密钥交换初始化时使用的对方的ID。
因为密钥交换的发起方和响应方的算法流程是一样的,因此将其封装为一个函数,只是在传入参数是需要区别一下己方和对方。
按照密钥交换流程,实现密钥交换算法:
TempKeyPair SM9::keyExchange_init(const string& masterPublicKey, const string& othId)
{
string sR, sr, hashH1, sg, sg3, sg0, bufferZ;
big h1 = NULL;
big r = NULL;
epoint *Ppube = NULL;
epoint *Qoth = NULL;
epoint *R = NULL;
#ifdef SELF_CHECK
string H1Hex, QothHex, rHex, RHex, gHex, g3Hex, g0Hex;
#endif
if( !mIsInit ) {
mErrorNum = SM9_ERROR_NOT_INIT;
throw exception(getErrorMsg().c_str());
}
Parameters::init_big(h1);
Parameters::init_big(r);
Parameters::init_epoint(Qoth);
Parameters::init_epoint(Ppube);
Parameters::init_epoint(R);
hashH1 = KGC::H1(othId, HID_KEYEXCHANGE);
Parameters::cin_big(h1, hashH1.c_str(), hashH1.length());
Parameters::cin_epoint(Ppube, masterPublicKey.c_str());
#ifdef SELF_CHECK
H1Hex = YY::YHex::bin2Hex(hashH1);
#endif
// Step1 : QB =[H1(IDB||hid, N)]P1 +Ppub-e or QA = [H1(IDA || hid, N)]P1 + Ppub-e
ecurve_mult(h1, Parameters::param_P1, Qoth);
ecurve_add(Ppube, Qoth);
#ifdef SELF_CHECK
QothHex = YY::YHex::bin2hex(Parameters::cout_epoint(Qoth));
#endif
#ifdef SELF_CHECK
if( othId=="Bob" )
rHex = YY::YHex::hex2bin("5879DD1D51E175946F23B1B41E93BA31C584AE59A426EC1046A4D03B06C8");
else
rHex = YY::YHex::hex2bin("018B98C44BEF9F8537FB7D071B2C928B3BC65BD3D69E1EEE213564905634FE");
Parameters::cin_big(r, rHex.c_str(), rHex.length());
#else
// Step2: generate r
bigrand(Parameters::param_N, r);
#endif
// Step3 : RA = [rA]QB or RB= [rB]QA
ecurve_mult(r, Qoth, R);
sr = Parameters::cout_big(r);
sR = Parameters::cout_epoint(R);
Parameters::release_big(h1);
Parameters::release_big(r);
Parameters::release_epoint(Qoth);
Parameters::release_epoint(Ppube);
Parameters::release_epoint(R);
return TempKeyPair(sr, sR);
}
KeyAgreement SM9::keyExchange(const string& masterPublicKey, bool isSponsor, const string& myId, const string& othId,
const string& myPrikey, const TempKeyPair& myTempKeyPair, const string& othTempPubkey, int klen)
{
KeyAgreement keyAgreement;
bool hasException = true;
string sKey, sg1, sg2, sg3, bufferZ, bufferTmp, srmy, sRmy, sRoth, SB1, SA2, tmp;
epoint *Ppube = NULL;
epoint *Qoth = NULL;
epoint *Rmy = NULL;
epoint *Roth = NULL;
big rmy = NULL;
ZZN12 g1;
ZZN12 g2;
ZZN12 g3;
ZZN12 gtmp;
ecn2 de;
YY::YSM3 sm3Digest;
#ifdef SELF_CHECK
string g1Hex, g2Hex, g3Hex;
#endif
if( !mIsInit ) {
mErrorNum = SM9_ERROR_NOT_INIT;
throw exception(getErrorMsg().c_str());
}
Parameters::init_epoint(Qoth);
Parameters::init_epoint(Ppube);
Parameters::init_epoint(Rmy);
Parameters::init_epoint(Roth);
Parameters::init_big(rmy);
Parameters::init_ecn2(de);
srmy = myTempKeyPair.getPrivate();
sRmy = myTempKeyPair.getPublic();
Parameters::cin_big(rmy, srmy.c_str(), srmy.length());
Parameters::cin_epoint(Rmy, sRmy.c_str());
sRoth = othTempPubkey;
Parameters::cin_epoint(Roth, sRoth.c_str());
// check R is on G1
if( !Parameters::isPointOnG1(Roth) ) {
mErrorNum = SM9_ERROR_KEYEXCHANGE_R_NOT_ON_G1;
goto END;
}
// StepA5_B4: g=e(Ppub-e,P2)^r
Parameters::cin_epoint(Ppube, masterPublicKey.c_str());
if( !ZZN12::calcRatePairing(gtmp, Parameters::param_P2, Ppube, Parameters::param_t, Parameters::norm_X) ) {
mErrorNum = SM9_ERROR_CALC_RATE;
goto END;
}
if( isSponsor )
g1 = gtmp.pow(rmy);
else
g2 = gtmp.pow(rmy);
// g=e(Roth,de)
Parameters::cin_ecn2_byte128(de, myPrikey.c_str());
if( !ZZN12::calcRatePairing(gtmp, de, Roth, Parameters::param_t, Parameters::norm_X) ) {
mErrorNum = SM9_ERROR_CALC_RATE;
goto END;
}
if( isSponsor )
g2 = gtmp;
else
g1 = gtmp;
// g3=g^r
if( isSponsor )
g3 = g2.pow(rmy);
else
g3 = g1.pow(rmy);
sg1 = g1.toByteArray();
sg2 = g2.toByteArray();
sg3 = g3.toByteArray();
#ifdef SELF_CHECK
g1Hex = YY::YHex::bin2Hex(sg1);
g2Hex = YY::YHex::bin2Hex(sg2);
g3Hex = YY::YHex::bin2Hex(sg3);
#endif
// Step6 : S1 or SB
bufferTmp.resize(0);
if( isSponsor ) {
bufferTmp.append(myId);
bufferTmp.append(othId);
bufferTmp.append(sRmy);
bufferTmp.append(sRoth);
} else {
bufferTmp.append(othId);
bufferTmp.append(myId);
bufferTmp.append(sRoth);
bufferTmp.append(sRmy);
}
bufferZ.resize(0);
bufferZ.append(sg2);
bufferZ.append(sg3);
bufferZ.append(bufferTmp);
sm3Digest.update(bufferZ);
sm3Digest.finish();
tmp = sm3Digest.getData();
bufferZ.resize(0);
bufferZ.append(1, (char)0x82);
bufferZ.append(sg1);
bufferZ.append(tmp);
sm3Digest.update(bufferZ);
sm3Digest.finish();
SB1 = sm3Digest.getData();
// StepA8_B7 : SA or S2
bufferZ[0] = (char)0x83;
sm3Digest.update(bufferZ);
sm3Digest.finish();
SA2 = sm3Digest.getData();
// StepA7_B5 : SKA or SKB
bufferZ.resize(0);
bufferZ.append(bufferTmp);
bufferZ.append(sg1);
bufferZ.append(sg2);
bufferZ.append(sg3);
sKey = KGC::KDF(bufferZ, klen);
keyAgreement = KeyAgreement(sKey, SA2, SB1);
hasException = false;
END:
Parameters::release_epoint(Qoth);
Parameters::release_epoint(Ppube);
Parameters::release_epoint(Rmy);
Parameters::release_epoint(Roth);
Parameters::release_big(rmy);
Parameters::release_ecn2(de);
if( hasException ) {
throw exception(getErrorMsg().c_str());
}
return keyAgreement;
}
总结算法实现,实际的密钥交换流程为: