国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。
SM1 为对称加密。其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
SM2为非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。
SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。
SM4 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。
由于SM1、SM4加解密的分组大小为128bit,故对消息进行加解密时,若消息长度过长,需要进行分组,要消息长度不足,则要进行填充。
SM2,SM3,SM4的相关文档可以参考如下链接:
http://218.241.108.63/wiki/index.php/首页
SM2,SM3,SM4的C代码如下:使用了openssl开源库。
http://files.cnblogs.com/files/TaiYangXiManYouZhe/Sm2_sm3_sm4_c语言实现.zip
当使用特定的芯片进行SM1或其他国密算法加密时,若用多个线程调用加密卡的API时,要考虑芯片对于多线程的支持情况。
以下为不使用openssl库的另一种实现方案,基于Miracl大数运算库,可移植。
主要难点就是移植Miracl库,裁剪配置,测试加解密算法。针对不同平台如32位或64位,以及平台的大小端进行配置。
如果Miracl库移植ok了,那么基于Miracl库的sm2算法应没多大问题。
Miracl库里文件较多,且从官网下载的代码,在linux系统上是很容易编译。
但是想用在单片机上,需要一些移植和配置。
只需要包含需要的文件就行了。
以下是编译过程。
然后需要新建一个sm2.c文件、sm2.h,用于实现sm2功能函数;一个sm3.c文件、一个sm3.h文件,用于实现sm3功能函数(之所以要增加sm3的功能是因为sm2算法中需要sm3计算hash值功能。 下面给出生成密钥对的示例:
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include <memory.h>
#include <time.h>
#include "sm2.h"
#define SM2_PAD_ZERO TRUE
#define SM2_DEBUG 0
struct FPECC{
char *p;
char *a;
char *b;
char *n;
char *x;
char *y;
};
/*SM2*/
struct FPECC Ecc256 = {
"8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3",
"787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498",
"63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A",
"8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7",
"421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D",
"0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2",
};
unsigned char radom1[] = { 0x4C,0x62,0xEE,0xFD,0x6E,0xCF,0xC2,0xB9,0x5B,0x92,0xFD,0x6C,0x3D,0x95,0x75,0x14,0x8A,0xFA,0x17,0x42,0x55,0x46,0xD4,0x90,0x18,0xE5,0x38,0x8D,0x49,0xDD,0x7B,0x4F };
void PrintBuf(unsigned char *buf, int buflen)
{
int i;
for (i = 0; i < buflen; i ) {
if (i % 32 != 31)
printf("x", buf[i]);
else
printf("xn", buf[i]);
}
printf("n");
//return 0;
}
void Printch(unsigned char *buf, int buflen)
{
int i;
for (i = 0; i < buflen; i ) {
if (i % 32 != 31)
printf("%c", buf[i]);
else
printf("%cn", buf[i]);
}
printf("n");
//return 0;
}
void sm2_keygen(unsigned char *wx, int *wxlen, unsigned char *wy, int *wylen,unsigned char *privkey, int *privkeylen)
{
struct FPECC *cfig = &Ecc256;
epoint *g;
big a,b,p,n,x,y,key1;
miracl *mip = mirsys(20,0);
mip->IOBASE = 16;
p = mirvar(0);
a = mirvar(0);
b = mirvar(0);
n = mirvar(0);
x = mirvar(0);
y = mirvar(0);
key1 = mirvar(0);
cinstr(p,cfig->p);
cinstr(a,cfig->a);
cinstr(b,cfig->b);
cinstr(n,cfig->n);
cinstr(x,cfig->x);
cinstr(y,cfig->y);
ecurve_init(a,b,p,MR_PROJECTIVE);
g = epoint_init();
epoint_set(x,y,0,g);
irand(time(NULL));
bigrand(n,key1); ////私钥db
ecurve_mult(key1,g,g); //计算Pb
epoint_get(g,x,y);
*wxlen = big_to_bytes(32, x, (char *)wx, TRUE);
*wylen = big_to_bytes(32, y, (char *)wy, TRUE);
*privkeylen = big_to_bytes(32, key1, (char *)privkey, TRUE);
mirkill(key1);
mirkill(p);
mirkill(a);
mirkill(b);
mirkill(n);
mirkill(x);
mirkill(y);
epoint_free(g);
mirexit();
}
int kdf(unsigned char *zl, unsigned char *zr, int klen, unsigned char *kbuf)
{
/*
return 0: kbuf = 0, 不可
1: kbuf 可
*/
unsigned char buf[70];
unsigned char digest[32];
unsigned int ct = 0x00000001; //初始化一个32比特构成的计数器ct=0x00000001
int i, m, n;
unsigned char *p;
memcpy(buf, zl, 32);
memcpy(buf 32, zr, 32);
m = klen / 32;
n = klen % 32;
p = kbuf;
for(i = 0; i < m; i )
{
buf[64] = (ct >> 24) & 0xFF;
buf[65] = (ct >> 16) & 0xFF;
buf[66] = (ct >> 8) & 0xFF;
buf[67] = ct & 0xFF;
sm3(buf, 68, p);
p = 32;
ct ;
}
/*对i从1到?klen/v?执行:b.1)计算Hai=Hv(Z ∥ ct);b.2) ct */
if(n != 0)
{
buf[64] = (ct >> 24) & 0xFF;
buf[65] = (ct >> 16) & 0xFF;
buf[66] = (ct >> 8) & 0xFF;
buf[67] = ct & 0xFF;
sm3(buf, 68, digest);
}
/*若klen/v是整数,令Ha!?klen/v? = Ha?klen/v?,否则令Ha!?klen/v?为Ha?klen/v?最左边的(klen ?
(v × ?klen/v?))比特*/
memcpy(p, digest, n);
/*令K = Ha1||Ha2|| ||*/
for(i = 0; i < klen; i )
{
if(kbuf[i] != 0)
break;
}
if(i < klen)
return 1;
else
return 0;
}
int sm2_encrypt(unsigned char *msg,int msglen, unsigned char *wx,int wxlen, unsigned char *wy,int wylen, unsigned char *outmsg)
{
struct FPECC *cfig = &Ecc256;
big x2, y2, c1, c2, k;
big a,b,p,n,x,y;
epoint *g, *w;
int ret = -1;
int i;
unsigned char zl[32], zr[32];
unsigned char *tmp;
miracl *mip;
tmp = malloc(msglen 64);
if(tmp == NULL)
return -1;
mip = mirsys(20, 0);
mip->IOBASE = 16;
p=mirvar(0);
a=mirvar(0);
b=mirvar(0);
n=mirvar(0);
x=mirvar(0);
y=mirvar(0);
k=mirvar(0);
x2=mirvar(0);
y2=mirvar(0);
c1=mirvar(0);
c2=mirvar(0);
cinstr(p,cfig->p);
cinstr(a,cfig->a);
cinstr(b,cfig->b);
cinstr(n,cfig->n);
cinstr(x,cfig->x);
cinstr(y,cfig->y);
ecurve_init(a,b,p,MR_PROJECTIVE);
g=epoint_init();
w=epoint_init();
epoint_set(x,y,0,g);
bytes_to_big(wxlen,(char *)wx,x);
bytes_to_big(wylen,(char *)wy,y);
epoint_set(x,y,0,w);
irand(time(NULL));
sm2_encrypt_again:
#if SM2_DEBUG
bytes_to_big(32, (char *)radom1, k);
#else
do
{
bigrand(n, k);
}
while (k->len == 0);
#endif
ecurve_mult(k, g, g);
epoint_get(g, c1, c2);
big_to_bytes(32, c1, (char *)outmsg, TRUE);
big_to_bytes(32, c2, (char *)outmsg 32, TRUE);
//计算椭圆曲线点C1
if(point_at_infinity(w))
goto exit_sm2_encrypt;
//计算椭圆曲线点S
ecurve_mult(k, w, w);
epoint_get(w, x2, y2);
big_to_bytes(32, x2, (char *)zl, TRUE);
big_to_bytes(32, y2, (char *)zr, TRUE);
//计算椭圆曲线点[k]PB
if (kdf(zl, zr, msglen, outmsg 64) == 0)
goto sm2_encrypt_again;
//计算t = KDF,如果t全零,返回A1
for(i = 0; i < msglen; i )
{
outmsg[64 i] ^= msg[i];
}
//计算C2
memcpy(tmp, zl, 32);
memcpy(tmp 32, msg, msglen);
memcpy(tmp 32 msglen, zr, 32);
sm3(tmp, 64 msglen, &outmsg[64 msglen]);
//计算C3
ret = msglen 64 32;
printf("key:");
cotnum(k, stdout);
//输出C,C在outmsg
exit_sm2_encrypt:
mirkill(x2);
mirkill(y2);
mirkill(c1);
mirkill(c2);
mirkill(k);
mirkill(a);
mirkill(b);
mirkill(p);
mirkill(n);
mirkill(x);
mirkill(y);
epoint_free(g);
epoint_free(w);
mirexit();
free(tmp);
return ret;
}
int sm2_decrypt(unsigned char *msg,int msglen, unsigned char *privkey, int privkeylen, unsigned char *outmsg)
{
struct FPECC *cfig = &Ecc256;
big x2, y2, c, k;
big a,b,p,n,x,y,key1;
epoint *g;
unsigned char c3[32];
unsigned char zl[32], zr[32];
int i, ret = -1;
unsigned char *tmp;
miracl *mip;
if(msglen < 96)
return 0;
msglen -= 96;
tmp = malloc(msglen 64);
if(tmp == NULL)
return 0;
mip = mirsys(20, 0);
mip->IOBASE = 16;
x2=mirvar(0);
y2=mirvar(0);
c=mirvar(0);
k = mirvar(0);
p = mirvar(0);
a = mirvar(0);
b = mirvar(0);
n = mirvar(0);
x = mirvar(0);
y = mirvar(0);
key1 = mirvar(0);
bytes_to_big(privkeylen,(char *)privkey,key1);
cinstr(p,cfig->p);
cinstr(a,cfig->a);
cinstr(b,cfig->b);
cinstr(n,cfig->n);
cinstr(x,cfig->x);
cinstr(y,cfig->y);
ecurve_init(a,b,p,MR_PROJECTIVE);
g = epoint_init();
bytes_to_big(32, (char *)msg, x);
bytes_to_big(32, (char *)msg 32, y);
if(!epoint_set(x,y,0,g))
goto exit_sm2_decrypt; //检验是否为椭圆曲线
if(point_at_infinity(g))
goto exit_sm2_decrypt; //计算S
ecurve_mult(key1, g, g);
epoint_get(g, x2, y2);
big_to_bytes(32, x2, (char *)zl, TRUE);
big_to_bytes(32, y2, (char *)zr, TRUE); //计算[db]c1
if (kdf(zl, zr, msglen, outmsg) == 0)
goto exit_sm2_decrypt; //计算t
for(i = 0; i < msglen; i )
{
outmsg[i] ^= msg[i 64];
} //计算M到outsmg
memcpy(tmp, zl, 32);
memcpy(tmp 32, outmsg, msglen);
memcpy(tmp 32 msglen, zr, 32);
sm3(tmp, 64 msglen, c3);//计算u
if(memcmp(c3, msg 64 msglen, 32) != 0)
goto exit_sm2_decrypt;
ret = msglen;
exit_sm2_decrypt:
mirkill(x2);
mirkill(y2);
mirkill(c);
mirkill(k);
mirkill(p);
mirkill(a);
mirkill(b);
mirkill(n);
mirkill(x);
mirkill(y);
mirkill(key1);
epoint_free(g);
mirexit();
free(tmp);
return ret;
}
int main()
{
printf("sm2 test....n");
unsigned char dB[] = { 0x16,0x49,0xAB,0x77,0xA0,0x06,0x37,0xBD,0x5E,0x2E,0xFE,0x28,0x3F,0xBF,0x35,0x35,0x34,0xAA,0x7F,0x7C,0xB8,0x94,0x63,0xF2,0x08,0xDD,0xBC,0x29,0x20,0xBB,0x0D,0xA0 };
unsigned char xB[] = { 0x43,0x5B,0x39,0xCC,0xA8,0xF3,0xB5,0x08,0xC1,0x48,0x8A,0xFC,0x67,0xBE,0x49,0x1A,0x0F,0x7B,0xA0,0x7E,0x58,0x1A,0x0E,0x48,0x49,0xA5,0xCF,0x70,0x62,0x8A,0x7E,0x0A };
unsigned char yB[] = { 0x75,0xDD,0xBA,0x78,0xF1,0x5F,0xEE,0xCB,0x4C,0x78,0x95,0xE2,0xC1,0xCD,0xF5,0xFE,0x01,0xDE,0xBB,0x2C,0xDB,0xAD,0xF4,0x53,0x99,0xCC,0xF7,0x7B,0xBA,0x07,0x6A,0x42 };
unsigned char tx[256];
unsigned char etx[256];
unsigned char mtx[256];
FILE *fp=0;
int wxlen, wylen, privkeylen,len;
//fopen(&fp, "5.txt", "r");
//len=fread_s(tx, 256,sizeof(unsigned char), 256, fp);
fp = fopen("5.txt","r");
len=fread(tx,1,256,fp);
tx[len] = "