资讯详情

【C语言】SM2公钥加密及解密算法的解析和代码实现

之前我是写了一篇文章,简单介绍了国密SM这里有三个算法,以及代码的实现链接。本文详细介绍了公钥加密算法和解密算法。

1 用到的符号

:两个用户使用公钥密码系统。 :Fq它们定义中的元素Fq上椭圆曲线E。 :用户B的私钥。 :Fq上椭圆曲线E 所有有理点(包括无限远点)O)组成的集合。 :有限域含有q个元素。 :椭圆曲线的一个基点是素数。 :密码杂凑函数。 :v比特的密码杂凑函数是新闻摘要的长度。 :密钥衍生函数。 :待加密的消息。 :解密得到的消息。 :基点G的阶(n是# E(Fq)因素)。 :椭圆曲线上的一个特殊点,称为无限远点或零点,是椭圆曲线加法组的单位元。 :用户B的公钥。 :有限域Fq中元素的数量。 :x与y的拼接,其中x、y可以是比特串或字节串。 :椭圆曲线上点Pk倍点,即,[k]P= P P · · · P(k个,k是正整数)。 :大于或等于x且小于或等于y的整数集合。 :顶函数,大于或等于x的最小整数。例如,7=7, ?8.3?=9。 :底函数,小于或等于x的最大整数。例如,7=7, ?8.3?=8。 :E(Fq)上点数称为椭圆曲线E(Fq)的阶。

2 参数及辅助函数

2.1 椭圆曲线系统参数

· 椭圆曲线系统的参数包括有限Fq的规模q; · 定义椭圆曲线E(Fq) 两个方程元素a、b∈Fq; · E(Fq)上的基点G=(xG,yG)(G≠0),其中xG和yG是Fq两个元素; · G阶n等可选项(如n的余因子h等)。

2.2 用户秘钥对

· 用户B的密钥对包括其私钥dB和公钥PB=[dB]G。

2.3 辅助函数

2.3.1 密码杂凑函数

· 本部分规定使用国家密码管理局批准的密码杂凑算法,如SM三密码杂凑算法。

2.3.2 秘钥衍生函数

密钥衍生函数的作用是从共享的秘密比特串中衍生密钥数据。在密钥协商过程中,密钥衍生函数作用于产生所需的会话密钥或进一步加密所需的密钥数据。 密码杂凑函数需要调用密钥衍生函数。 设置密码杂凑函数为Hv( ),其输出是长度为v比特的杂凑值。 · 输入:比特串Z,整数klen(表示要获得的密钥数据的比特长度小于(232-1)v)。 · 输出:长度为klen密钥数据比特串K。 a)由32比特组成的初始化计数器ct=0x00000001; b)对i从1到?klen/v?执行: b.1)计算Hai=Hv(Z ∥ ct); b.2) ct ; c)若klen/v是整数,令Ha!?klen/v? = Ha?klen/v?,否则令Ha!?klen/v?为Ha?klen/v?最左边的(klen ? (v × ?klen/v?))比特; d)令K = Ha1||Ha2|| · · · ||Ha?klen/v??1||Ha!?klen/v?。 代码示例:

void KDF(uint8_t* result, uint8_t* Z, int dataLenInBit, int keyLenInBit) { 
             uint8_t* tmpRes = (uint8_t*)malloc(keyLenInBit / 8), * tmpData = (uint8_t*)malloc(dataLenInBit / 8   4);      keyLenInBit /= 8;     dataLenInBit /= 8;//转为byte数     memset(tmpRes, 0, keyLenInBit); memset(tmpData, 0, dataLenInBit + 4); uint32_t ct = 1; //a.初始化32bit计数器 uint8_t hash[32] = { 
          0 }, ct2Byte[4] = { 
          0 }; //ct2Byte与计数器的值一样,只是为了方便计算 int realHashLen = 32; //摘要长度 int maxLoopNum = (keyLenInBit + 31) / 32; //times = [klenInBit/v](向上取整) int i = 0; memcpy(tmpData, Z, dataLenInBit); for (i = 0; i < maxLoopNum; i++) { 
          ct2Byte[0] = (ct >> 24) & 0xFF; ct2Byte[1] = (ct >> 16) & 0xFF; ct2Byte[2] = (ct >> 8) & 0xFF; ct2Byte[3] = (ct) & 0xFF; memcpy(tmpData + dataLenInBit, ct2Byte, 4); // Z || ct SM3(tmpData, dataLenInBit + 4, hash); //b. 计算Hai = Hv(Z || ct) if (i == maxLoopNum - 1) //c. 根据keylen/32是否整除,截取摘要的值 { 
          if (keyLenInBit % 32 != 0) { 
          realHashLen = keyLenInBit % 32; } } memcpy(tmpRes + 32 * i, hash, realHashLen); //d. 每次把结果连接上 i++; ct++; } if (result != NULL) { 
          memcpy(result, tmpRes, keyLenInBit); } free(tmpRes); free(tmpData); } 

3 加密算法及流程

加密算法流程

uint8_t* SM2Encrypt(char* messagePlain, int messageSizeInBit, EccPoint* pubKey)
{ 
        
    uint256_t randomK = { 
         0 };
    EccPoint* pointC1 = malloc(sizeof(EccPoint)), * kPb = malloc(sizeof(EccPoint));
    uint8_t* t = malloc(messageSizeInBit / 8), * x2 = malloc(64), * y2 = malloc(32),//t = KDF()
        * C1 = malloc(65), * x1 = malloc(32), * y1 = malloc(32),
        * C2 = malloc(messageSizeInBit / 8), * C3 = malloc(64 + messageSizeInBit / 8),
        * C = malloc(65 + messageSizeInBit / 8 + 32);
}

    设需要发送的消息为比特串M,klen为M的比特长度。     为了对明文M进行加密,作为加密者的用户A应实现以下运算步骤: :用随机数发生器产生随机数k∈[1,n-1];

	GetRandomNumber(randomK, Ec->p);

GetRandomNumber:

void GetRandomNumber(uint64_t* result, uint64_t* maxVal)
{ 
        
    for (int i = 0; i < 4; i++)
    { 
        
        result[i] = ((uint64_t)rand() << 60) | ((uint64_t)rand() << 45) | ((uint64_t)rand() << 30) | ((uint64_t)rand() << 15) | ((uint64_t)rand() << 0);
    }
    result[3] %= (maxVal[3] - 1);
}

:计算椭圆曲线点C1=[k]G=(x1,y1),并将C1的数据类型转换为比特串;

    CalculateC1(pointC1, randomK, G);
    Uint256ToString(x1, pointC1->x);
    Uint256ToString(y1, pointC1->y);
    C1[0] = 0x04;
    memcpy(C1 + 1, x1, 32);
    memcpy(C1 + 33, y1, 32);//C1 = 04||x1||y1

CalculateC1:

void CalculateC1(EccPoint* result, uint256_t k, EccPoint* G)
{ 
        
    CurvePointMul(result, G, k);
}

:计算椭圆曲线点S=[h]PB,若S是无穷远点,则报错并退出; :计算椭圆曲线点[k]PB=(x2,y2),并将坐标x2、y2 的数据类型转换为比特串;

    CalculateKPb(kPb, randomK, pubKey);
    Uint256ToString(x2, kPb->x);
    Uint256ToString(y2, kPb->y);
void CalculateKPb(EccPoint* result, uint256_t k, EccPoint* Pb)
{ 
        
    CurvePointMul(result, Pb, k);
}

void Uint256ToString(uint8_t* res, uint256_t num)
{ 
        
    int  pos = 0;

    for (int i = 3; i >= 0; i--)
    { 
        
        for (int j = 7; j >= 0; j--)
        { 
        
            res[pos++] = (num[i] & (0xffULL << (j * 8))) >> (j * 8);
        }
    }
}

:计算t=KDF(x2 ∥ y2, klen),若t为全0比特串,则返回A1;

    memcpy(x2 + 32, y2, 32);//x2 = x2 || y2
    KDF(t, x2, 64 * 8, messageSizeInBit);	//t = KDF()

KDF:

void KDF(uint8_t* result, uint8_t* Z, int dataLenInBit, int keyLenInBit)
{ 
        
    uint8_t* tmpRes = (uint8_t*)malloc(keyLenInBit / 8), * tmpData = (uint8_t*)malloc(dataLenInBit / 8 + 4);

    keyLenInBit /= 8;
    dataLenInBit /= 8;//转为byte数
    memset(tmpRes, 0, keyLenInBit);
    memset(tmpData, 0, dataLenInBit + 4);

    uint32_t ct = 1;  //a.初始化32bit计数器
    uint8_t hash[32] = { 
         0 }, ct2Byte[4] = { 
         0 }; //ct2Byte与计数器的值一样,只是为了方便计算
    int realHashLen = 32; //摘要长度
    int maxLoopNum = (keyLenInBit + 31) / 32; //times = [klenInBit/v](向上取整)
    int i = 0;

    memcpy(tmpData, Z, dataLenInBit);
    for (i = 0; i < maxLoopNum; i++)
    { 
        
        ct2Byte[0] = (ct >> 24) & 0xFF;
        ct2Byte[1] = (ct >> 16) & 0xFF;
        ct2Byte[2] = (ct >> 8) & 0xFF;
        ct2Byte[3] = (ct) & 0xFF;
        memcpy(tmpData + dataLenInBit, ct2Byte, 4);	// Z || ct
        SM3(tmpData, dataLenInBit + 4, hash);	//b. 计算Hai = Hv(Z || ct)

        if (i == maxLoopNum - 1) //c. 根据keylen/32是否整除,截取摘要的值
        { 
        
            if (keyLenInBit % 32 != 0)
            { 
        
                realHashLen = keyLenInBit % 32;
            }
        }
        memcpy(tmpRes + 32 * i, hash, realHashLen);	//d. 每次把结果连接上
        i++;
        ct++;
    }

    if (result != NULL)
    { 
        
        memcpy(result, tmpRes, keyLenInBit);
    }
    free(tmpRes);
    free(tmpData);
}

:计算C2 = M ⊕ t;

    memcpy(x2 + 32, y2, 32);//x2 = x2 || y2
    KDF(t, x2, 64 * 8, messageSizeInBit);	//t = KDF()
    CalculateC2(C2, messagePlain, t, messageSizeInBit / 8);	//C2 = M^t

CalculateC2:

void CalculateC2(uint8_t* result, char* M, uint8_t* t, int lenInByte)
{ 
        
    for (int i = 0; i < lenInByte; i++)
    { 
        
        result[i] = M[i] ^ t[i];
    }
}

:计算C3 = Hash(x2 ∥ M ∥ y2);

SM3((uint8_t*)C3, 64 + messageSizeInBit / 8, (uint8_t*)C3);	//C3 = SM3(x2||M||y2)

:输出密文C = C1 ∥ C2 ∥ C3

    memcpy(C, C1, 65);
    memcpy(C + 65, C2, messageSizeInBit / 8);
    memcpy(C + 65 + messageSizeInBit / 8, C3, 32);	//C = C1 || C2 || C3

4 解密算法及流程

uint8_t* SM2Decrypt(char* C, int lenInByte, uint64_t* privKey)
{ 
        
	EccPoint* pointC1 = malloc(sizeof(EccPoint)), * point2 = malloc(sizeof(EccPoint));
    int lenOfMsg = lenInByte - 65 - 32;
    uint8_t* C2 = malloc(lenOfMsg + 1), * msg = malloc(lenOfMsg + 1),
        * x1 = malloc(32), * y1 = malloc(32),
        * x2 = malloc(64), * y2 = malloc(32), * t = malloc(lenOfMsg);
}

设klen为密文中C2的比特长度。 为了对密文C=C1 ∥ C2 ∥ C3 进行解密,作为解密者的用户B应实现以下运算步骤: :从C中取出比特串C1,将C1的数据类型转换为椭圆曲线上的点,验证C1是否满足椭圆曲线方程,若不满足则报错并退出; :计算椭圆曲线点S=[h]C1,若S是无穷远点,则报错并退出; :计算[dB]C1=(x2,y2),将坐标x2、y2的数据类型转换为比特串;

	msg[lenInByte - 65 - 32] = '\0';
    memcpy(C2, C + 65, lenInByte - 65 - 32);//密文msg
    memcpy(x1, C + 1, 32);
    memcpy(y1, C + 1 + 32, 32);
    StringToUint256(pointC1->x, x1);
    StringToUint256(pointC1->y, y1);
    CurvePointMul(point2, pointC1, privKey);

:计算t=KDF(x2 ∥ y2, klen),若t为全0比特串,则报错并退出;

 Uint256ToString(x2, point2->x);
    Uint256ToString(y2, point2->y);
    memcpy(x2 + 32, y2, 32);	//x2 = x2 || y2
    KDF(t, x2, 64 * 8, (lenInByte - 65 - 32) * 8);//t = KDF(x2||y2,klenInBit)

:从C中取出比特串C2,计算M′ = C2 ⊕ t;

    CalculateMessage(msg, C2, t, lenOfMsg);	//M' = C2 ^ t

CalculateMessage:

void CalculateMessage(uint8_t* result, uint8_t* C2, uint8_t* t, int lenInByte)
{ 
        
    for (int i = 0; i < lenInByte; i++)
    { 
        
        result[i] = C2[i] ^ t[i];
    }
}

:计算u = Hash(x2 ∥ M′ ∥ y2),从C中取出比特串C3,若u ≠ C3,则报错并退出; :输出明文M′。

return msg;

5 运行结果

        char* testMessage = "The Little Prince is a novella by French aristocrat, writer.\n\ It was first published in English and French in the US by Reynal & Hitchcock in April 1943.\ \nThe story follows a young prince who visits various planets in space, including Earth, and addresses themes of loneliness, friendship, love, \ and loss.Despite its style as a children's book, The Little Prince makes observations about life and human nature.";
        int lenTestMessage = strlen(testMessage);
		/** * ... * 省略公钥生成等部分 * ... */
        uint8_t* resC = SM2Encrypt(testMessage, lenTestMessage * 8, pubKey);
        uint8_t* resSM2Decrypt = SM2Decrypt((char*)resC, 65 + 32 + lenTestMessage * 8 / 8, privKey);
        printf("Decryption %s\n", strcmp(testMessage, resSM2Decrypt) ? "failed" : "passed");

结果: 可见解密出的文本和加密前文本一致。

标签: y2p连接器连接器fq18

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台