SM2加密
stm32f407 - SM2加密
·
一、前提描述
库地址:https://github.com/guanzhi/GmSSL/releases/tag/v3.1.1
设备环境:STM32F407裸机
公钥类型:非压缩公钥( 0x04 || x || y );
密文长度:96+明文长度:C1(64字节(x:32B + y:32B)) + C3(32字节) + C2(明文长度nB);
二、粗略描述加密过程:
(1)获得公钥x和y
1.如果是非压缩公钥04开头,那么接着64字节分别是公钥x与y,直接使用即可;
2.如果是base64格式,需要先decode得到十六进制公钥,再判断是否为非压缩公钥;
3.如果不是非压缩公钥,那么不需要继续往后看了,本文只描述非压缩公钥加密;
(2)加密数据C1
使用随机数计算得到,固定64字节(C1_x:32B + C1_y:32B)。如果每次加密得到C1数据都是一样的,那么产生随机数函数有误;
(3)加密数据C2和C3
1.继续使用随机数K与公钥P获得派生x2/y2(跟公钥x/y不一样);(K*P=(x2,y2),没看懂源码如何计算);
2.t = KDF(x2 || y2, inlen),也是没看懂源码如何计算。应该是用派生公钥x2y2,与明文长度inlen经过一系列计算,初步得到暂时的C2密文t。并且密文不能全是0;
3.C2 = M xor t,也是没看懂源码如何计算。最终得到C2加密数据,C2数据长度与明文长度一致;
4.C3 = Hash(x2 || M || y2),用hash算法将派生公钥和明文计算得到C3,这更看不懂hash算法的源码;
(4)密文编码
经过上面步骤,已经完整得到C1 C2 C3的密文。最后需要将这三部分数据拼接起来(拼接顺序本文只阐述新标准C1C3C2),其中有两种密文的编码方式:
ASN.1
序列码0x30 + 接着的数据长度值 + C1_x(序列码0x02 + 接着的数据长度值 + C1_x数据) + C1_y(序列码0x02 + 接着的数据长度值 + C1_y数据) + C3(序列码0x04 + 接着的数据长度值 + C3数据) + C2(序列码0x04 + 接着的数据长度值 + C2数据);
注意:拼接C1_的时候,如果遇到第一字节最高位为1的时候,要在前面加0,实际密文长度+1(源码就是这样做的,没看懂为啥要这样做)
plain
序列码0x04 + C1数据 + C3数据 + C2数据;
注意:只是前面加了0x04序列码,后面数据全是原始密文拼接
三、代码实现
(1)密钥对
测试公钥:043DF5A2FD63FE9B9ACEEED91F7908B684E0568B0ADF3AA9E847EE1F877501049CD8DDB764CD34A71CC127675117CA9C0B59DF3DD072DF88089DEA4511A8ABA039
测试私钥:5408B17606AF7594F9C13113B432CD824D889C8250CF5E7EF2341FBC6D1726A5
加密数据:o5bi5n6zr3zkyhyb
格式说明:测试使用的公钥和私钥都是十六进制的;加密数据类型是字符串,长度16字节;
(2)SM2加密代码
void SM2EncryptData(uint8_t* src, uint8_t* dst)
{
uint8_t u8Temp = 0;
uint8_t dIndex = 0;
uint8_t encodeBuf[153] = {0};
uint8_t base64Buf[144] = {0};
uint8_t sm2Buf[144] = {0};
SM2_KEY sm2_key;
size_t clen = 0;
size_t mlen = 153;
int err = 0;
// 初始化密钥
memset(&sm2_key, 0, sizeof(SM2_KEY));
memcpy(sm2_key.public_key.x, param.nvparam.value.encryptInfo.SM2encryptKey_x, 32);
memcpy(sm2_key.public_key.y, param.nvparam.value.encryptInfo.SM2encryptKey_y, 32);
// memcpy(sm2_key.private_key, key_private, 32);
// SM2加密
err = sm2_encrypt(&sm2_key, src, strlen((char*)src), sm2Buf, &clen);
// // SM2解密测试
// err = sm2_decrypt(&sm2_key, sm2Buf, clen, mbuf, &mlen);
#if 1
/*
处理加密数据ASN.1 -> plain:
1.ASN.1序列头0x30 与 剩余长度
2.C1数据0x02 (0x20/0x21), C1固定32+32字节
3.C3数据0x04 0x20, 固定32字节
4.C2数据0x04 0x??, 长度与明文长度一致
*/
base64Buf[0] = 0x04; // plain编码 序列头
if( sm2Buf[dIndex] == 0x30 )
{
// C1_x
dIndex += 3;
if( sm2Buf[dIndex] == 0x20 )
{
dIndex++;
memcpy(&base64Buf[1], &sm2Buf[dIndex], 32);
}
else if( sm2Buf[dIndex] == 0x21 && sm2Buf[dIndex+1] == 0x00 )
{
dIndex += 2;
memcpy(&base64Buf[1], &sm2Buf[dIndex], 32);
}
dIndex += 32;
// C1_y
dIndex++;
if( sm2Buf[dIndex] == 0x20 )
{
dIndex++;
memcpy(&base64Buf[33], &sm2Buf[dIndex], 32);
}
else if( sm2Buf[dIndex] == 0x21 && sm2Buf[dIndex+1] == 0x00 )
{
dIndex += 2;
memcpy(&base64Buf[33], &sm2Buf[dIndex], 32);
}
dIndex += 32;
// C3
dIndex += 2;
memcpy(&base64Buf[65], &sm2Buf[dIndex], 32);
dIndex += 32;
// C2
dIndex++;
u8Temp = sm2Buf[dIndex++];
memcpy(&base64Buf[97], &sm2Buf[dIndex], u8Temp);
}
#endif
// 加密数据转 base64
err = base64_encode(encodeBuf, &mlen, base64Buf, 97+u8Temp);
memcpy(dst, encodeBuf, mlen);
}
(3)密文输出
ASN.1:30 79 02 20 5F A6 54 03 91 76 3C 8D 7A 0B E8 87 29 D5 6C 24 CC 65 84 F2 4A 01 3B A9 15 04 DA 7A 3D 10 D4 32 02 21 00 FA A4 37 A5 3F 89 A0 0F 17 C2 F2 77 18 11 FF 3A 8D 9F D0 89 96 CF AC 84 FA 6B 27 37 5F 36 C6 C9 04 20 C7 60 E0 3E 1C D8 D1 8C 93 CE AD 60 C5 91 BC 25 3B 7E 97 F2 D0 98 11 F5 A0 25 1A 88 DE 83 5A 20 04 10 63 19 7A 72 8A 5A 41 B5 7F AC 39 12 C2 94 D4 B9
plain: 04 5F A6 54 03 91 76 3C 8D 7A 0B E8 87 29 D5 6C 24 CC 65 84 F2 4A 01 3B A9 15 04 DA 7A 3D 10 D4 32 FA A4 37 A5 3F 89 A0 0F 17 C2 F2 77 18 11 FF 3A 8D 9F D0 89 96 CF AC 84 FA 6B 27 37 5F 36 C6 C9 C7 60 E0 3E 1C D8 D1 8C 93 CE AD 60 C5 91 BC 25 3B 7E 97 F2 D0 98 11 F5 A0 25 1A 88 DE 83 5A 20 63 19 7A 72 8A 5A 41 B5 7F AC 39 12 C2 94 D4 B9
数据说明:该加密库将以ASN.1编码格式返回密文(十六进制),根据实际需要是否转为plain编码
代码注意:加密数据存放数组sm2Buf要根据实际明文长度定义;上面代码sm2Buf保存的是ASN.1编码的密文;base64Buf保存的是plain编码的密文;encodeBuf是将密文转为base64格式
(4)验证密文
解密工具:https://tool.hiofd.com/sm2-decrypt-online/
如下图,解密成功:

更多推荐



所有评论(0)