1. 程式人生 > >小王的尷尬日常(三)--Openssl 實現國密演算法(加密和解密)

小王的尷尬日常(三)--Openssl 實現國密演算法(加密和解密)

上一次講了產生金鑰,這次我們講一下加密解密的實現。
先說一下加密解密的流程,一下這些內容都是從國密局釋出的國密標準文件裡面摘錄出來的。大家可以去國密局的網站上自己下載。

下列符號適用於本部分。
A,B:使用公鑰密碼系統的兩個使用者。
a,b: Fq中的元素,它們定義Fq上的一條橢圓曲線E。
dB:使用者B的私鑰。
E(Fq): Fq上橢圓曲線E 的所有有理點(包括無窮遠點O)組成的集合。
Fq
:包含q個元素的有限域。
G:橢圓曲線的一個基點,其階為素數。
2Hash( ):密碼雜湊函式。
Hv
( ):訊息摘要長度為v位元的密碼雜湊函式。
KDF( ):金鑰派生函式。
M :待加密的訊息。
M ′:解密得到的訊息。
n:基點G的階(n是# E(Fq)的素因子)。
O:橢圓曲線上的一個特殊點,稱為無窮遠點或零點,是橢圓曲線加法群的單位元。
PB:使用者B的公鑰。
q:有限域Fq中元素的數目。
x∥y: x與y的拼接,其中x、 y可以是位元串或位元組串。
[k]P:橢圓曲線上點P的k倍點,即, [k]P= P + P + · · · + P(k個, k是正整數)。
[x,y]:大於或等於x且小於或等於y的整數的集合。
⌈x⌉:頂函式,大於或等於x的最小整數。例如⌈7⌉=7, ⌈8.3⌉=9。
⌊x⌋:底函式,小於或等於x的最大整數。例如⌊7⌋=7, ⌊8.3⌋=8。
#E(Fq): E(Fq)上點的數目,稱為橢圓曲線E(Fq)的階

加密過程

設需要傳送的訊息為位元串M, klen為M的位元長度。
為了對明文M進行加密,作為加密者的使用者A應實現以下運算步驟:
A1:用隨機數發生器產生隨機數k∈[1,n-1];
A2:計算橢圓曲線點C1=[k]G=(x1,y1),按本文字第1部分4.2.8和4.2.4給出的細節,將C1的資料類
型轉換為位元串;
A3:計算橢圓曲線點S=[h]PB,若S是無窮遠點,則報錯並退出;
A4:計算橢圓曲線點[k]PB=(x2,y2),按本文字第1部分4.2.5和4.2.4給出的細節,將座標x2、 y2 的
資料型別轉換為位元串;
A5:計算t=KDF(x2 ∥ y2, klen),若t為全0位元串,則返回A1;
A6:計算C2 = M ⊕ t;
A7:計算C3 = Hash(x2 ∥ M ∥ y2);
A8:輸出密文C = C1 ∥ C2 ∥ C3。

道人家給畫好了,我們需要做的就是把它轉化成程式碼….對了,還有一張流程圖。
加密流程圖

接下來上程式碼:

    unsigned char* t, *hm;
    BIGNUM* rand;
    EC_POINT* rG, *rK;
    BIGNUM *rKx, *rKy, *rGx, *rGy;

    unsigned char bK[65] = {0};
    unsigned char C3[33] = {0};

    rG = EC_POINT_new(this->mGroup);
    rK = EC_POINT_new(this->mGroup);
    rand = BN_new();

    //隨機數k∈[1,n-1]
BN_rand_range(rand, this->z); //C1=[k]G=(x1,y1) EC_POINT_mul(this->mGroup, rG, NULL, this->mGP, rand, this->ctx); rGx = BN_new(); rGy = BN_new(); if(!EC_POINT_get_affine_coordinates_GFp(this->mGroup, rG, rGx, rGy, this->ctx)) { return -3; } BN_bn2bin(rGx, pd); BN_bn2bin(rGy, &pd[32]); //[k]PB=(x2,y2) EC_POINT_mul(this->mGroup, rK, NULL, EC_KEY_get0_public_key(this->mKey), rand, this->ctx); rKx = BN_new(); rKy = BN_new(); if(!EC_POINT_get_affine_coordinates_GFp(this->mGroup, rK, rKx, rKy, this->ctx)) { return -3; } //t=KDF(x2||y2, klen) BN_bn2bin(rKx, bK); BN_bn2bin(rKy, &bK[32]); t = new BYTE[elen + 1]; memset(t, 0, elen + 1); this->mKDF(bK, 64, elen, t); for (int i = elen; i--;) { t[i] = t[i]^pe[i]; } //C3 = Hash(x2||M||y2) hm = new unsigned char[elen + 65]; memset(hm, 0, elen + 65); memcpy(hm, bK, 32); memcpy(&hm[32], pe, elen); memcpy(&hm[elen + 32], &bK[32], 32); hash(hm, elen + 64, C3, "sha256"); //C = C1||C2||C3 memcpy(&pd[64], t, elen); memcpy(&pd[64 + elen], C3, 32); delete[] t; delete[] hm; t = NULL; hm = NULL; EC_POINT_free(rG); EC_POINT_free(rK); return 0;

如果細心的朋友會發現我少了兩步:
1.A3(校驗rK這個點的)
2.校驗 是否t為0
怎麼說呢….因為我比較懶,而且也沒有這個必要,因為我們用的是openssl,以上兩種情況不會出現,所以就省略了,當然加上也無可厚非。

加密之後我們就要解密了,能拆就能立能砸就能砌。
解密流程

解密演算法
設klen為密文中C2的位元長度。
為了對密文C=C1 ∥ C2 ∥ C3 進行解密,作為解密者的使用者B應實現以下運算步驟:
B1:從C中取出位元串C1,按本文字第1部分4.2.3和4.2.9給出的細節,將C1的資料型別轉換為橢
圓曲線上的點,驗證C1是否滿足橢圓曲線方程,若不滿足則報錯並退出;
B2:計算橢圓曲線點S=[h]C1,若S是無窮遠點,則報錯並退出;
B3:計算[dB]C1=(x2,y2),按本文字第1部分4.2.5和4.2.4給出的細節,將座標x2、 y2的資料型別轉
換為位元串;
B4:計算t=KDF(x2 ∥ y2, klen),若t為全0位元串,則報錯並退出;
B5:從C中取出位元串C2,計算M ′ = C2 ⊕ t;
B6:計算u = Hash(x2 ∥ M ′ ∥ y2),從C中取出位元串C3,若u ̸= C3,則報錯並退出;
B7:輸出明文M ′。

解密流程圖

下面是解密的程式碼


    unsigned char* t, *c2, *hm;
    unsigned char bC1x[65] = {0};
    unsigned char bC1y[65] = {0};
    unsigned char bK[65] = {0};
    unsigned char u[33] = {0}; 

    unsigned int mlen, hm_len;  

    EC_POINT *rG, *rK;
    BIGNUM *C1x, *C1y, *rKx, *rKy;

    //取出rG
    C1x = BN_new();
    C1y = BN_new();

    memcpy(&bC1x[32], pe, 32);
    memcpy(&bC1y[32], &pe[32], 32);

    BN_bin2bn(bC1x, 64, C1x);
    BN_bin2bn(bC1y, 64, C1y);

    rG = EC_POINT_new(this->mGroup);
    if(!EC_POINT_set_affine_coordinates_GFp(this->mGroup, 
        rG, C1x, C1y, this->ctx))
    {
        EC_POINT_free(rG);
        return -1;
    }

    //求得rK
    rK = EC_POINT_new(this->mGroup);
    EC_POINT_mul(this->mGroup, rK, NULL, rG, 
        EC_KEY_get0_private_key(this->mKey), 
        this->ctx);

    rKx = BN_new();
    rKy = BN_new();
    if(!EC_POINT_get_affine_coordinates_GFp(this->mGroup, 
        rK, rKx, rKy, this->ctx))
    {
        EC_POINT_free(rG);
        EC_POINT_free(rK);
        return -2;
    }

    //求取hv 解密 
    BN_bn2bin(rKx, bK);
    BN_bn2bin(rKy, &bK[32]);

    mlen = elen - 96;

    c2 = new unsigned char[mlen + 1];
    memset(c2, 0, mlen + 1);
    memcpy(c2, &pe[64], mlen);

    t = new unsigned char[mlen + 1];
    memset(t, 0, mlen + 1);
    this->mKDF(bK, 64, elen - 96, t);

    for (int i = elen - 96; i--;)
    {
        t[i] = t[i]^c2[i];
    }

    hm_len = mlen + 64;
    hm = new unsigned char[hm_len + 1];
    memset(hm, 0,hm_len + 1);

    BN_bn2bin(rKx, hm);
    memcpy(&hm[32], t, mlen);
    BN_bn2bin(rKy, &hm[32 + mlen]);

    //校驗hash值
    hash(hm, hm_len, u, "sha256");
    for (int i = 0; i < 32;i++)
    {
        if (u[i] != pe[elen - 32 + i])
        {
            EC_POINT_free(rG);
            EC_POINT_free(rK);

            delete[] t;
            delete[] c2;
            delete[] hm;

            t = NULL;
            c2 = NULL;
            hm = NULL;

            return -3;
        }   
    }

    memcpy(pd, t, mlen);

    EC_POINT_free(rG);
    EC_POINT_free(rK);

    delete[] t;
    delete[] c2;
    delete[] hm;

    t = NULL;
    c2 = NULL;
    hm = NULL;

    return 0;

這就是加密和解密的過程了。