SMS4分組加密體制的分析及其實現(xiàn)
SMS4是國內公布的無線局域網(wǎng)分組加密算法,由于其安全性高、加密速度快的優(yōu)點,經常被應用到社會的各個領域。下面我就給大家介紹一下這種分組加密算法。
一、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)。輪函數(shù)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為反序變換。
輪函數(shù)中使用的子密鑰由主密鑰
MK=(MK0,MK1,MK2,MK3)、系統(tǒng)參數(shù)FK=(FK0,F(xiàn)K1,F(xiàn)K2,F(xiàn)K3)和固定參數(shù)CKi(i,0,1,...,31)根據(jù)以下密鑰擴展算法生成(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′與加密算法輪函數(shù)結構F相似,只是其中的T′置換與T置換變換略有不同。輪函數(shù)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算法的代碼實現(xiàn)
本文采用C++編寫SMS4算法的程序:
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- const?int?TextNum?=?4;//分組長度4*32
- const?int?KeyNum?=?32;//輪密鑰的個數(shù)
- const?int?RoundNum?=?32;//輪數(shù)
- //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之間,貌似發(fā)生了內存的覆蓋,
- //計算了一次后保存的key,在后面沒有操作,結果其值竟然發(fā)生了改變,
- //而換了個地方之后,就沒有這個問題了
- 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的個數(shù)
- FILE?*fp;
- int?main(int?argc,?char?*argv[])
- {
- ????char?mode;
- ????char?filename[256];
- ????unsigned?char?keyString[20];
- ????if(argc?==?1)
- ????{
- ????????printf("參數(shù)個數(shù)有錯誤!程序需在控制臺下運行\(zhòng)n");
- ????????printf("假設生成的可執(zhí)行文件為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("參數(shù)有錯誤!\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中\(zhòng)n",fileOut);
- ????????else
- ????????????printf("解密完成,明文已輸出到%s中\(zhòng)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("連續(xù)施加S盒變換,變換?%d?次時出現(xiàn)輸出等于輸入(十進制)\n",?index_i+1);
- ????????????????break;
- ????????????}
- ????????????else
- ????????????????S_in?=?S_out;
- ????????}
- ????????if(index_i?==?1000000)
- ????????????printf("連續(xù)施加S盒變換,變換?%d?次也沒有出現(xiàn)輸出等于輸入,應該為無窮多次\n",?index_i);
- ????????return?0;
- ????}
- ????return?0;
- }
- int?comp_diff(unsigned?int?A,unsigned?int?B,?int?len)//統(tǒng)計兩個int型的不同的bit的個數(shù),len為統(tǒng)計的長度
- {
- ????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)));
- ????//優(yōu)先級,||與|是不同的
- ????return?C;
- }
SMS4體制的安全性在于S盒的設置,S盒是分組密碼中的重要組成部分,是大多數(shù)分組密碼中唯一的非線性結構,在很大程度上決定分組密碼的安全性。通過分析,SMS4加密算法S盒擁有一些較好的安全特性。
小知識之分組加密
分組密碼是將明文消息編碼表示后的數(shù)字(簡稱明文數(shù)字)序列,劃分成長度為n的組(可看成長度為n的矢量),每組分別在密鑰的控制下變換成等長的輸出數(shù)字(簡稱密文數(shù)字)序列。










