橢圓曲線的密鑰交換協議ECDH


ECDH使得交換雙方可以在不共享任何秘密的情況下協商出一個密鑰。密鑰磋商過程:

 假設密鑰交換雙方為Alice、Bob,有相同的橢圓曲線。

   1) Alice生成隨機數私鑰a,計算a*G。 生成Alice公鑰

   2) Bob生成隨機數私鑰b,計算b*G。 生成Bob公鑰

   3) Alice將公鑰a*G和基點G傳遞給Bob。竊聽者C可以獲取公鑰a*G和基點G。

   4) Bob將b*G傳遞給Alice。同理,竊聽者C同樣可以獲得b*G。

   5) Bob收到Alice傳遞過來的公鑰a*G,計算Q =baG;

   6) Alice收到Bob傳遞的公鑰b*G,計算Q=abG

     竊聽者C可以獲得G、aG、bG但是得不到abG

 雙方生成密鑰對后,密鑰對放在EVP_PKEY*pkey所指的對象中。需要將公鑰從pkey中取出然后轉為8進制數據發送給對方。

  1)從pkey中拿到原始接口EC_KEY格式的密鑰

struct ec_key_st *EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey);

  2)取出公鑰,公鑰是一個點

EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key);

  3)開始轉換

size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *p,
                          point_conversion_form_t form,
                          unsigned char *buf, size_t len, BN_CTX *ctx);

  group:橢圓曲線的參數,包含G a b 等信息,可以通過EC_KEY_get0_group(EVP_PKEY*pkey)拿到;

  p:公鑰;

  form:數據存放格式。由以下三種格式,選擇一種即可

typedef enum {
        /** the point is encoded as z||x, where the octet z specifies
         *  which solution of the quadratic equation y is  */
    POINT_CONVERSION_COMPRESSED = 2,
        /** the point is encoded as z||x||y, where z is the octet 0x04  */
    POINT_CONVERSION_UNCOMPRESSED = 4,
        /** the point is encoded as z||x||y, where the octet z specifies
         *  which solution of the quadratic equation y is  */
    POINT_CONVERSION_HYBRID = 6
} point_conversion_form_t;

  buf:轉換成功后存放空間;

  len:空間大小;

  ctx:上下文,如果只做一次轉換,可以填0;

收到8進制數據的密鑰需要轉回到EVP_PKEY*類型

  1)雙方使用的是同樣的橢圓曲線,然后生成密鑰對EVP_PKEY*類型的pkey,通過這個pkey可以拿到當前橢圓曲線的參數group。

struct ec_key_st *EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey);
const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key);

  2)將8進制數據轉為POINT*類型

//創建EC_POINT類型的對象,后面要釋放
EC_POINT* EC_POINT_new(const EC_GROUP*group);
//將公鑰放在P中
int EC_POINT_oct2point(const EC_GROUP *group, EC_POINT *p,
                       const unsigned char *buf, size_t len, BN_CTX *ctx);

  此時公鑰已經存放在p中

  3)創建一個橢圓曲線的低級、原始接口ec_key,橢圓曲線的公私鑰和參數都存放在這個結構中。

EC_KEY *EC_KEY_new(void);
//將橢圓曲線的參數放在ec_key結構中
int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group);
//將公鑰也放進去
int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub);

  以上完成后,對方的基點和公鑰已經放在了EC_KEY中。

  4)將原始接口轉為EVP_PKEY結構

EVP_PKEY *EVP_PKEY_new(void);
int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, struct ec_key_st *key);

  生成共享密鑰。下面演示了如何將一個私鑰/公鑰對(保存在pkey變量中)和某個對等體的公鑰(保存在peerkey變量中)組合以導出共享秘密(保存在key變量中,其長度保存在keylen中)。

//使用pkey和ENGINE e中指定的算法分配公鑰算法上下文
EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
//初始化密鑰交換
int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx);
//設定對方公鑰
int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer);
//計算的共享密鑰放在key中
int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);

完整代碼,模擬雙方協商密鑰

.h

class XECDH
{
public:
    XECDH();
    //生成橢圓曲線密鑰對
    bool CreateKey();
    //將自己生成pkey轉為8進制字符串,准備發送給對方
    int GetPubKey(unsigned char *pubkey);
        //收到對方的數據后,轉為evp_pkey
    EVP_PKEY* OctTokey(const  unsigned char *pubkey, int pubkey_size);
    //生成共享密鑰
    int SharedKey(unsigned char *out, const unsigned char *ppkey, int key_size);

private:
    int nid;  //橢圓曲線的nid
    //密鑰對存放
    EVP_PKEY*pkey = nullptr;
};

.cpp

XECDH::XECDH()
{
    nid = NID_secp256k1;
}

bool XECDH::CreateKey()
{
    //生成橢圓曲線的參數的上下文,用來生成對應的參數
    auto ctx=EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
    int re = EVP_PKEY_paramgen_init(ctx);
    if (re != 1)
    {
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return false;
    }
    //選擇橢圓曲線
    re=EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid);
    if (re != 1)
    {
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return false;
    }
    //生成橢圓曲線參數存儲到params
    EVP_PKEY*param = nullptr;
    re=EVP_PKEY_paramgen(ctx, &param);
    if (re != 1)
    {
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return false;
    }
    EVP_PKEY_CTX_free(ctx);
    //生成密鑰對
    //根據EC參數生成密鑰對創建上下文
    auto kctx=EVP_PKEY_CTX_new(param,NULL);
    if (!kctx)
    {
        ERR_print_errors_fp(stderr);
        return false;
    }
    //生成密鑰對的初始化
    re=EVP_PKEY_keygen_init(kctx);
    if (re != 1)
    {
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(kctx);
        return false;
    }
    re=EVP_PKEY_keygen(kctx, &pkey);
    EVP_PKEY_CTX_free(kctx);
    if (re != 1)
    {
        ERR_print_errors_fp(stderr);
        return false;
    }
    cout << "generkey success!" << endl;

    return true;
}

int XECDH::GetPubKey(unsigned char * pubkey)
{
    if (!pkey)
        return 0;
    //從pkey中拿到原始密鑰
    auto key=EVP_PKEY_get0_EC_KEY(pkey);
    //拿到公鑰,這是一個點
    auto pub = EC_KEY_get0_public_key(key);
    //將這個點轉換成8進制
    int re=EC_POINT_point2oct(EC_KEY_get0_group(key), pub, 
POINT_CONVERSION_HYBRID, pubkey, 1024, 0); return re; } EVP_PKEY * XECDH::OctTokey(const unsigned char * pubkey, int pubkey_size) { //拿到當前橢圓曲線參數,里面有G,abp auto key = EVP_PKEY_get0_EC_KEY(pkey); auto group = EC_KEY_get0_group(key); //pubkey-->EC_POINT EC_POINT*p = EC_POINT_new(group);//公鑰 EC_POINT_oct2point(group, p, pubkey, pubkey_size, 0);//將對方的公鑰已經存儲在P中 //橢圓曲線的低級、原始接口ec_key,橢圓曲線的公私鑰和參數都存放在這個結構中 EC_KEY*ec_key = EC_KEY_new(); //將參數填充到ec_key中 EC_KEY_set_group(ec_key, group);//選擇同樣的橢圓曲線 EC_KEY_set_public_key(ec_key, p);//將公鑰也填充到ec_key中 EC_POINT_free(p); //將原始接口轉為高級接口evp_pkey EVP_PKEY *ppkey = EVP_PKEY_new(); EVP_PKEY_set1_EC_KEY(ppkey, ec_key); EC_KEY_free(ec_key); return ppkey; } int XECDH::SharedKey(unsigned char * out, const unsigned char * ppkey, int key_size) { //生成一個上下文 //函數使用pkey和ENGINE e中指定的算法分配公鑰算法上下文。 auto ctx = EVP_PKEY_CTX_new(pkey,0); if (!ctx) { ERR_print_errors_fp(stderr); return 0; } //初始化密鑰交換 int er=EVP_PKEY_derive_init(ctx); if (er != 1) { EVP_PKEY_CTX_free(ctx); ERR_print_errors_fp(stderr); return 0; } //設定對方公鑰 bG 和私鑰a int re=EVP_PKEY_derive_set_peer(ctx, OctTokey(ppkey, key_size)); if (re != 1) { EVP_PKEY_CTX_free(ctx); ERR_print_errors_fp(stderr); return 0; } //開始計算 size_t outsize = 1024; re=EVP_PKEY_derive(ctx, out, &outsize); if (re != 1) { EVP_PKEY_CTX_free(ctx); ERR_print_errors_fp(stderr); return 0; } EVP_PKEY_CTX_free(ctx); return outsize; }

main.cpp

int main()
{
    XECDH server;
    XECDH client;
        //存放sererv端生成公鑰的8進制數據
    unsigned char spub[1024] = { 0 };
       //存放clinet 端生成公鑰的8進制數據
    unsigned char cpub[1024] = { 0 };
        //server生成的共享密鑰
    unsigned char serversharedkey[1024] = { 0 };
       //client生成的共享密鑰
    unsigned char clintsharedkey[1024] = { 0 };
    //server生成一個密鑰對
    server.CreateKey();
    //server將密鑰對轉為8進制數據
    int seroctlen = server.GetPubKey(spub);
        //////////////////////////////////////////////////
        ////////client端收到了server發送來的公鑰///////
        /////////////////////////////////////////////////
    client.CreateKey();
    int clioctlen = client.GetPubKey(cpub);

    server.SharedKey(serversharedkey, cpub, clioctlen);
    client.SharedKey(clintsharedkey, spub, seroctlen);
    cout << "serevr生成的共享密鑰:" << serversharedkey << endl;
    cout << "client生成共享密鑰:" << clintsharedkey << endl;
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM