SMS4是国内公布的无线局域网分组加密算法,由于其安全性高、加密速度快的优点,经常被应用到社会的各个领域。下面我就给大家介绍一下这种分组加密算法。
一、SMS4加密算法简介
分组加密算法SMS4的分组长度为128比特,密钥长度为128比特。加密算法与密钥扩展算法都采用32轮非线性迭代结构。解密过程与加密过程的结构相似,只是轮密钥的使用顺序相反。SMS4算法将明文和密文均看成4个32比特字,即明文(X0,X1,X2,X30∈(Z232)4,密文(Y0,Y1,Y2,Y3)∈(Z232)4,各轮子密钥为一个32比特字:rki∈Z232(i=1,2,...,31)。轮函数F作用于前4轮的输出结果Xi,Xi+1,Xi+2,Xi+3和当前子密钥rki,输出一个32比特字Xi+4=F(Xi,Xi+1,Xi+2,Xi+3,rki),Xi+T,Xi+1+Xi+2+Xi+3+rki)(i=1,2,...,31)。最后4轮输出的逆序作为密文(Y0,Y1,Y1,Y2)=R(X32,X33,X34,X35)=(X35,X34,X33,X32),其中R为反序变换。
轮函数中使用的子密钥由主密钥
MK=(MK0,MK1,MK2,MK3)、系统参数FK=(FK0,FK1,FK2,FK3)和固定参数CKi(i,0,1,...,31)根据以下密钥扩展算法生成(FKi,CKi均为32比特字):
首先,(K0,K1,K2,K3,...,MK0+FK0,MK1+FK1,MK2+FK2,MK3+FK3);
然后,对于i=0,1,...,31,rki=Ki+4=F′(Ki=Ki+1=Ki+2=Ki+3=CKi)=Ki+T′(Ki=1+Ki+2+Ki=3+CKi)。
不难看到,密钥扩展算法结构F′与加密算法轮函数结构F相似,只是其中的T′置换与T置换变换略有不同。轮函数F中Z232→Z232上的可逆置换T由非线性变换τ和线性变换L复合而成,即T(·)=L(τ(·)),τ由4个并行S盒构成,τ(a0,a1,a2,a3)=(Sbox(a0),Sbox(a1),Sbox(a2),Sbox(a3))=(b0,b1,b2,b3)(ai,bi∈Z28);L对τ的输出B∈Z232实施移位和异或操作:L(B)=B+(B<<<2)+(B<<<10)+(B<<<18)+(B<<<24)=C,B=b0b1b2b3。而密钥扩展算法的可逆置换T′由同样的非线性变换τ和简化的线性变换L′(B)=B+(B<<<13)+(B<<<23)复合而成。
二、SMS4算法的代码实现
本文采用C++编写SMS4算法的程序:
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- const int TextNum = 4;//分组长度4*32
- const int KeyNum = 32;//轮密钥的个数
- const int RoundNum = 32;//轮数
- //const int DECRYPT = 1;
- //unsigned int plain_text[TextNum];
- unsigned int X[TextNum]={0};//明文
- unsigned int Y[TextNum]={0};//密文
- unsigned int MK[TextNum]={0};//密钥
- unsigned int K[36]={0};//中间结果
- unsigned int key[KeyNum]={0};//轮密钥
- //刚开始将key放在了X与Y之间,貌似发生了内存的覆盖,
- //计算了一次后保存的key,在后面没有操作,结果其值竟然发生了改变,
- //而换了个地方之后,就没有这个问题了
- unsigned int S_Box_Table[16][16] = {
- {0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x5},
- {0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x4,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x6,0x99},
- {0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0xb,0x43,0xed,0xcf,0xac,0x62},
- {0xe4,0xb3,0x1c,0xa9,0xc9,0x8,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6},
- {0x47,0x7,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8},
- {0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0xf,0x4b,0x70,0x56,0x9d,0x35},
- {0x1e,0x24,0xe,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x1,0x21,0x78,0x87},
- {0xd4,0x0,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x2,0xe7,0xa0,0xc4,0xc8,0x9e},
- {0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1},
- {0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3},
- {0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0xd,0x53,0x4e,0x6f},
- {0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x3,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51},
- {0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8},
- {0xa,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0},
- {0x89,0x69,0x97,0x4a,0xc,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x9,0xc5,0x6e,0xc6,0x84},
- {0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48}};
- unsigned int FK[TextNum] = {0xA3B1BAC6,0x56AA3350,0x677D9197,0xB27022DC};
- unsigned int CK[RoundNum] = {
- 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
- 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
- 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
- 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
- 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
- 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
- 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
- 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279};
- void encrypt();//加、解密
- unsigned int T(unsigned int A);
- void S_Box( unsigned int &A, unsigned int &B);//S盒置换
- void genKeys(char type);//密钥编排
- unsigned int T2(unsigned int A);
- void getMessage(int &flag);//读入文件
- int comp_diff(unsigned int A,unsigned int B, int len);//比较A和B得到不同bit的个数
- FILE *fp;
- int main(int argc, char *argv[])
- {
- char mode;
- char filename[256];
- unsigned char keyString[20];
- if(argc == 1)
- {
- printf("参数个数有错误!程序需在控制台下运行\n");
- printf("假设生成的可执行文件为sms4.exe\n");
- printf("考虑加解密时,输入的是ASCII字符串,十六进制仅作样例\n");
- printf("样例测试请输入:sms4 s\n");
- printf("加密e.txt,使用密钥1234567890abcdef,输入:sms4 e e.txt 1234567890abcdef\n");
- printf("解密ESe.txt,使用密钥1234567890abcdef,输入:sms4 d ESe.txt 1234567890abcdef\n");
- exit(0);
- }
- mode = *argv[1];
- if(mode!='s')
- {
- if(argc != 4)
- {
- printf("参数有错误!\n");
- exit(0);
- }
- strcpy(filename,argv[2]);
- if(strlen(argv[3])!= 16)
- {
- printf("密钥长度不正确!");
- exit(0);
- }
- strcpy((char *)keyString,argv[3]);
- if((fp=fopen(filename,"rb")) == NULL)
- {
- printf("文件无法打开");
- exit(0);
- }
- int inc = 0,index = 0;
- for(index =0 ; index < 4; index++)
- {
- MK[index] = 0 ;
- for(inc = 0 ;inc < 4; inc++)
- MK[index] |= (((unsigned int)keyString[index * 4 + inc])<<((3-inc)<<3));
- }
- FILE *fpOut;
- char fileOut[200];
- if(mode == 'e')
- strcat(strcpy(fileOut,"ES\0"),filename);
- else
- strcat(strcpy(fileOut,"DS\0"),filename+2);
- fpOut= fopen(fileOut,"wb");
- int flag = 1;//判断是否结束的标志
- genKeys(mode);//生成密钥序列
- while(flag)
- {
- getMessage(flag);
- if(flag == 0)
- break;
- encrypt();//加密或解密
- for(index = 0 ; index < 4; index++)
- {
- Y[index] = (Y[index]>>24)|((Y[index]&0x00ff0000)>>8)|((Y[index]&0x0000ff00)<<8)|((Y[index]&0x000000ff)<<24);
- fwrite(Y + index,1 , 4 ,fpOut);
- }
- }
- if(mode == 'e')
- printf("加密完成,密文已输出到%s中\n",fileOut);
- else
- printf("解密完成,明文已输出到%s中\n",fileOut);
- fclose(fp);
- fclose(fpOut);
- }
- else //实际测试时,样例测试这部分没用,应该删除
- {
- printf("样例测试,显示为16进制:\n");
- X[0] = 0x01234567;
- X[1] = 0x89abcdef;
- X[2] = 0xfedcba98;
- X[3] = 0x76543210;
- printf("明文:%08x %08x %08x %08x\n",X[0],X[1],X[2],X[3]);
- MK[0] = 0x01234567;
- MK[1] = 0x89abcdef;
- MK[2] = 0xfedcba98;
- MK[3] = 0x76543210;
- printf("密钥:%08x %08x %08x %08x\n",MK[0],MK[1],MK[2],MK[3]);
- genKeys('e');
- encrypt();
- printf("加密一次,密文:%08x %08x %08x %08x\n",Y[0],Y[1],Y[2],Y[3]);
- int index = 0 ;
- for (index = 0; index < 1000000; index++)
- {
- encrypt();
- X[0] = Y[0];
- X[1] = Y[1];
- X[2] = Y[2];
- X[3] = Y[3];
- }
- printf("相同密钥加密百万次,密文:%08x %08x %08x %08x\n",Y[0],Y[1],Y[2],Y[3]);
- printf("*****************************************************************\n");
- X[0] = 0x01234567;
- X[1] = 0x89abcdef;
- X[2] = 0xfedcba98;
- X[3] = 0x76543210;
- printf("明文:%08x %08x %08x %08x\n",X[0],X[1],X[2],X[3]);
- int index_i = 0, index_j = 0;
- unsigned int con = 0x01;
- unsigned int cipher[TextNum] = {Y[0],Y[1],Y[2],Y[3]};
- unsigned int count_diff_sum = 0;
- unsigned int diff = 0;
- for(index_i = 0; index_i < 4; index_i++)
- {
- for(index_j = 31 ; index_j >= 0 ; index_j--)//原来有问题
- {
- X[index_i] ^= (con << index_j);
- encrypt();
- int index_t = 0;
- for(index_t = 0; index_t < 4; index_t++)
- {
- diff = comp_diff(cipher[index_t],Y[index_t],32);
- printf("%d ",diff);
- count_diff_sum += diff;
- }
- X[index_i] ^= (con << index_j);
- }
- printf("\n");
- }
- printf("明文输入改变1位,密文输出平均改变 %d 位(十进制)\n",count_diff_sum/128);
- printf("*****************************************************************\n");
- unsigned int S_in = X[2]&0x000000ff;
- printf("S box输入为%x \n", S_in);
- unsigned int S_out = 0;
- unsigned int S_ans = 0;
- S_Box(S_in,S_ans);
- S_ans &= 0x000000ff;
- printf("S box输出为%x \n", S_ans);
- count_diff_sum = 0;
- diff = 0;
- for(index_j = 7 ; index_j >= 0 ; index_j--)
- {
- S_in ^= (con << index_j);
- S_Box(S_in,S_out);
- S_out &= 0x000000ff;
- diff = comp_diff(S_out,S_ans,8);
- printf("%d ",diff);
- count_diff_sum += diff;
- S_in ^= (con << index_j);
- }
- printf("\nS盒输入改变1位,S盒输出平均改变 %d 位(十进制)\n",count_diff_sum/8);
- printf("*****************************************************************\n");
- S_in = X[1];
- printf("L 输入为%08x \n", S_in);
- S_out = 0;
- S_ans = 0;
- S_ans = (S_in ^ ((S_in<<2)|(S_in>>30))^((S_in<<10)|(S_in>>22))^((S_in<<18)|(S_in>>14))^((S_in<<24)|(S_in>>8)));
- printf("L 输出为%08x \n", S_ans);
- count_diff_sum = 0;
- diff = 0;
- for(index_j = 31 ; index_j >= 0 ; index_j--)
- {
- S_in ^= (con << index_j);
- S_ans = (S_in ^ ((S_in<<2)|(S_in>>30))^((S_in<<10)|(S_in>>22))^((S_in<<18)|(S_in>>14))^((S_in<<24)|(S_in>>8)));
- diff = comp_diff(S_out,S_ans,32);
- printf("%d ",diff);
- count_diff_sum += diff;
- S_in ^= (con << index_j);
- }
- printf("\nL 输入改变1位,L 输出平均改变 %d 位(十进制)\n",count_diff_sum/32);
- printf("*****************************************************************\n");
- S_in = X[1]&0x000000ff;
- printf("S_box 输入为%x \n", S_in);
- S_ans = S_in;
- S_out = 0;
- index_i = 0;
- for(index_i = 0; index_i < 1000000; index_i++)
- {
- S_Box(S_in,S_out);
- S_out &= 0x000000ff;
- if(S_out == S_ans)
- {
- printf("连续施加S盒变换,变换 %d 次时出现输出等于输入(十进制)\n", index_i+1);
- break;
- }
- else
- S_in = S_out;
- }
- if(index_i == 1000000)
- printf("连续施加S盒变换,变换 %d 次也没有出现输出等于输入,应该为无穷多次\n", index_i);
- return 0;
- }
- return 0;
- }
- int comp_diff(unsigned int A,unsigned int B, int len)//统计两个int型的不同的bit的个数,len为统计的长度
- {
- unsigned int C = A^B;
- int index = 0,ans = 0;
- unsigned int con = 0x01;
- for(index = 0; index < len ; index++ )
- {
- if(C&(con << index))
- ans++;
- }
- return ans;
- }
- void getMessage(int &flag)//读取输入
- {
- memset(X,0,sizeof(X));
- int inflag = 1;
- int index = 0;
- while(inflag && index < 4)
- {
- X[index] = 0;
- inflag = fread((void *)(X + index),1,4,fp);
- if(index == 0 && inflag == 0)
- flag = 0;
- index++;
- }
- for(index = 0 ; index < 4; index++)
- {
- X[index] = (X[index]>>24)|((X[index]&0x00ff0000)>>8)|((X[index]&0x0000ff00)<<8)|((X[index]&0x000000ff)<<24);
- }
- }
- void encrypt()//加密或解密
- {
- int index = 0;
- for(index = 0 ; index < RoundNum; index++)
- {
- X[index + 4] = (X[index]^T(X[index+1]^X[index+2]^X[index+3]^key[index]));
- }
- Y[0] = X[35];
- Y[1] = X[34];
- Y[2] = X[33];
- Y[3] = X[32];
- }
- unsigned int T(unsigned int A)
- {
- unsigned int B,C;
- S_Box(A, B);
- C = (B ^ ((B<<2)|(B>>30))^((B<<10)|(B>>22))^((B<<18)|(B>>14))^((B<<24)|(B>>8)));
- return C;
- }
- void S_Box( unsigned int &A, unsigned int &B)//S box置换
- {
- int index = 3;
- unsigned short Temp = 0;
- unsigned int ans = 0;
- B = 0;
- for (index = 3 ; index >= 0; index--)
- {
- Temp = (A>>(index<<3))&0x00ff;
- ans = (unsigned short)S_Box_Table[Temp>>4][Temp&0x0f];
- B|=(ans<<(index<<3));
- }
- }
- void genKeys(char type)//密钥编排
- {
- K[0] = MK[0]^FK[0];
- K[1] = MK[1]^FK[1];
- K[2] = MK[2]^FK[2];
- K[3] = MK[3]^FK[3];
- int index = 0 ;
- for ( index = 0 ; index < RoundNum; index++)
- {
- K[index + 4] = K[index] ^ T2(K[index+1]^K[index+2]^K[index+3]^CK[index]);
- key[index] = K[index + 4];
- }
- if(type == 'd')//解密时,将密钥反序
- {
- unsigned int Temp = 0 ;
- for (index = 0 ; index < RoundNum/2; index++)
- {
- Temp = key[index];
- key[index] = key[31 - index];
- key[31 - index] = Temp;
- }
- }
- // for( index =0 ;index < RoundNum; index++)
- // printf("%0x ", key[index]);
- // printf("\n");
- }
- unsigned int T2(unsigned int A)
- {
- unsigned int B,C;
- S_Box(A, B);
- C = (B ^ ((B<<13)|(B>>19))^((B<<23)|(B>>9)));
- //优先级,||与|是不同的
- return C;
- }
SMS4体制的安全性在于S盒的设置,S盒是分组密码中的重要组成部分,是大多数分组密码中唯一的非线性结构,在很大程度上决定分组密码的安全性。通过分析,SMS4加密算法S盒拥有一些较好的安全特性。
小知识之分组加密
分组密码是将明文消息编码表示后的数字(简称明文数字)序列,划分成长度为n的组(可看成长度为n的矢量),每组分别在密钥的控制下变换成等长的输出数字(简称密文数字)序列。