SMS4是国内公布的无线局域网分组加密算法,由于其安全性高、加密速度快的优点,经常被应用到社会的各个领域。下面我就给大家介绍一下这种分组加密算法。

一、SMS4加密算法简介

分组加密算法SMS4的分组长度为128比特,密钥长度为128比特。加密算法与密钥扩展算法都采用32轮非线性迭代结构。解密过程与加密过程的结构相似,只是轮密钥的使用顺序相反。SMS4算法将明文和密文均看成4个32比特字,即明文(X0,X1,X2,X30∈(Z2324,密文(Y0,Y1,Y2,Y3)∈(Z2324,各轮子密钥为一个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算法的程序:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. const int TextNum = 4;//分组长度4*32
  5. const int KeyNum = 32;//轮密钥的个数
  6. const int RoundNum = 32;//轮数
  7. //const int DECRYPT = 1;
  8. //unsigned int plain_text[TextNum];
  9. unsigned int X[TextNum]={0};//明文
  10. unsigned int Y[TextNum]={0};//密文
  11. unsigned int MK[TextNum]={0};//密钥
  12. unsigned int K[36]={0};//中间结果
  13. unsigned int key[KeyNum]={0};//轮密钥
  14. //刚开始将key放在了X与Y之间,貌似发生了内存的覆盖,
  15. //计算了一次后保存的key,在后面没有操作,结果其值竟然发生了改变,
  16. //而换了个地方之后,就没有这个问题了
  17. unsigned int S_Box_Table[16][16] = {
  18. {0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x5},
  19. {0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x4,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x6,0x99},
  20. {0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0xb,0x43,0xed,0xcf,0xac,0x62},
  21. {0xe4,0xb3,0x1c,0xa9,0xc9,0x8,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6},
  22. {0x47,0x7,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8},
  23. {0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0xf,0x4b,0x70,0x56,0x9d,0x35},
  24. {0x1e,0x24,0xe,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x1,0x21,0x78,0x87},
  25. {0xd4,0x0,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x2,0xe7,0xa0,0xc4,0xc8,0x9e},
  26. {0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1},
  27. {0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3},
  28. {0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0xd,0x53,0x4e,0x6f},
  29. {0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x3,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51},
  30. {0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8},
  31. {0xa,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0},
  32. {0x89,0x69,0x97,0x4a,0xc,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x9,0xc5,0x6e,0xc6,0x84},
  33. {0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48}};
  34. unsigned int FK[TextNum] = {0xA3B1BAC6,0x56AA3350,0x677D9197,0xB27022DC};
  35. unsigned int CK[RoundNum] = {
  36. 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
  37. 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
  38. 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
  39. 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
  40. 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
  41. 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
  42. 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
  43. 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279};
  44. void encrypt();//加、解密
  45. unsigned int T(unsigned int A);
  46. void S_Box( unsigned int &A, unsigned int &B);//S盒置换
  47. void genKeys(char type);//密钥编排
  48. unsigned int T2(unsigned int A);
  49. void getMessage(int &flag);//读入文件
  50. int comp_diff(unsigned int A,unsigned int B, int len);//比较A和B得到不同bit的个数
  51. FILE *fp;
  52. int main(int argc, char *argv[])
  53. {
  54.     char mode;
  55.     char filename[256];
  56.     unsigned char keyString[20];
  57.     if(argc == 1)
  58.     {
  59.         printf("参数个数有错误!程序需在控制台下运行\n");
  60.         printf("假设生成的可执行文件为sms4.exe\n");
  61.         printf("考虑加解密时,输入的是ASCII字符串,十六进制仅作样例\n");
  62.         printf("样例测试请输入:sms4 s\n");
  63.         printf("加密e.txt,使用密钥1234567890abcdef,输入:sms4 e e.txt 1234567890abcdef\n");
  64.         printf("解密ESe.txt,使用密钥1234567890abcdef,输入:sms4 d ESe.txt 1234567890abcdef\n");
  65.         exit(0);
  66.     }
  67.     mode = *argv[1];
  68.     if(mode!='s')
  69.     {
  70.         if(argc != 4)
  71.         {
  72.             printf("参数有错误!\n");
  73.             exit(0);
  74.         }
  75.         strcpy(filename,argv[2]);
  76.         if(strlen(argv[3])!= 16)
  77.         {
  78.             printf("密钥长度不正确!");
  79.             exit(0);
  80.         }
  81.         strcpy((char *)keyString,argv[3]);
  82.         if((fp=fopen(filename,"rb")) == NULL)
  83.         {
  84.             printf("文件无法打开");
  85.             exit(0);
  86.         }
  87.         int inc = 0,index = 0;
  88.         for(index =0 ; index < 4; index++)
  89.         {
  90.             MK[index] = 0 ;
  91.             for(inc = 0 ;inc < 4; inc++)
  92.                 MK[index] |= (((unsigned int)keyString[index * 4 + inc])<<((3-inc)<<3));
  93.         }
  94.         FILE *fpOut;
  95.         char fileOut[200];
  96.         if(mode == 'e')
  97.             strcat(strcpy(fileOut,"ES\0"),filename);
  98.         else
  99.             strcat(strcpy(fileOut,"DS\0"),filename+2);
  100.         fpOut= fopen(fileOut,"wb");
  101.         int flag = 1;//判断是否结束的标志
  102.         genKeys(mode);//生成密钥序列
  103.         while(flag)
  104.         {
  105.             getMessage(flag);
  106.             if(flag == 0)
  107.                 break;
  108.             encrypt();//加密或解密
  109.             for(index = 0 ; index < 4; index++)
  110.             {
  111.                 Y[index] = (Y[index]>>24)|((Y[index]&0x00ff0000)>>8)|((Y[index]&0x0000ff00)<<8)|((Y[index]&0x000000ff)<<24);
  112.                 fwrite(Y + index,1 , 4 ,fpOut);
  113.             }
  114.         }
  115.         if(mode == 'e')
  116.             printf("加密完成,密文已输出到%s中\n",fileOut);
  117.         else
  118.             printf("解密完成,明文已输出到%s中\n",fileOut);
  119.         fclose(fp);
  120.         fclose(fpOut);
  121.     }
  122.     else        //实际测试时,样例测试这部分没用,应该删除
  123.     {
  124.         printf("样例测试,显示为16进制:\n");
  125.         X[0] = 0x01234567;
  126.         X[1] = 0x89abcdef;
  127.         X[2] = 0xfedcba98;
  128.         X[3] = 0x76543210;
  129.         printf("明文:%08x %08x %08x %08x\n",X[0],X[1],X[2],X[3]);
  130.         MK[0] = 0x01234567;
  131.         MK[1] = 0x89abcdef;
  132.         MK[2] = 0xfedcba98;
  133.         MK[3] = 0x76543210;
  134.         printf("密钥:%08x %08x %08x %08x\n",MK[0],MK[1],MK[2],MK[3]);
  135.         genKeys('e');
  136.         encrypt();
  137.         printf("加密一次,密文:%08x %08x %08x %08x\n",Y[0],Y[1],Y[2],Y[3]);
  138.         int index = 0 ;
  139.         for (index = 0; index < 1000000; index++)
  140.         {
  141.             encrypt();
  142.             X[0] = Y[0];
  143.             X[1] = Y[1];
  144.             X[2] = Y[2];
  145.             X[3] = Y[3];
  146.         }
  147.         printf("相同密钥加密百万次,密文:%08x %08x %08x %08x\n",Y[0],Y[1],Y[2],Y[3]);
  148.         printf("*****************************************************************\n");
  149.         X[0] = 0x01234567;
  150.         X[1] = 0x89abcdef;
  151.         X[2] = 0xfedcba98;
  152.         X[3] = 0x76543210;
  153.         printf("明文:%08x %08x %08x %08x\n",X[0],X[1],X[2],X[3]);
  154.         int index_i = 0, index_j = 0;
  155.         unsigned int con = 0x01;
  156.         unsigned int cipher[TextNum] = {Y[0],Y[1],Y[2],Y[3]};
  157.         unsigned int count_diff_sum = 0;
  158.         unsigned int diff = 0;
  159.         for(index_i = 0; index_i < 4; index_i++)
  160.         {
  161.             for(index_j = 31 ; index_j >= 0 ; index_j--)//原来有问题
  162.             {
  163.                 X[index_i] ^= (con << index_j);
  164.                 encrypt();
  165.                 int index_t = 0;
  166.                 for(index_t = 0; index_t < 4; index_t++)
  167.                 {
  168.                     diff = comp_diff(cipher[index_t],Y[index_t],32);
  169.                     printf("%d ",diff);
  170.                     count_diff_sum += diff;
  171.                 }
  172.                 X[index_i] ^= (con << index_j);
  173.             }
  174.             printf("\n");
  175.         }
  176.         printf("明文输入改变1位,密文输出平均改变 %d 位(十进制)\n",count_diff_sum/128);
  177.         printf("*****************************************************************\n");
  178.         unsigned int S_in = X[2]&0x000000ff;
  179.         printf("S box输入为%x \n", S_in);
  180.         unsigned int S_out = 0;
  181.         unsigned int S_ans = 0;
  182.         S_Box(S_in,S_ans);
  183.         S_ans &= 0x000000ff;
  184.         printf("S box输出为%x \n", S_ans);
  185.         count_diff_sum = 0;
  186.         diff = 0;
  187.         for(index_j = 7 ; index_j >= 0 ; index_j--)
  188.         {
  189.             S_in ^= (con << index_j);
  190.             S_Box(S_in,S_out);
  191.             S_out &= 0x000000ff;
  192.             diff = comp_diff(S_out,S_ans,8);
  193.             printf("%d ",diff);
  194.             count_diff_sum += diff;
  195.             S_in ^= (con << index_j);
  196.         }
  197.         printf("\nS盒输入改变1位,S盒输出平均改变 %d 位(十进制)\n",count_diff_sum/8);
  198.         printf("*****************************************************************\n");
  199.         S_in = X[1];
  200.         printf("L 输入为%08x \n", S_in);
  201.         S_out = 0;
  202.         S_ans = 0;
  203.         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)));
  204.         printf("L 输出为%08x \n", S_ans);
  205.         count_diff_sum = 0;
  206.         diff = 0;
  207.         for(index_j = 31 ; index_j >= 0 ; index_j--)
  208.         {
  209.             S_in ^= (con << index_j);
  210.             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)));
  211.             diff = comp_diff(S_out,S_ans,32);
  212.             printf("%d ",diff);
  213.             count_diff_sum += diff;
  214.             S_in ^= (con << index_j);
  215.         }
  216.         printf("\nL 输入改变1位,L 输出平均改变 %d 位(十进制)\n",count_diff_sum/32);
  217.         printf("*****************************************************************\n");
  218.         S_in = X[1]&0x000000ff;
  219.         printf("S_box 输入为%x \n", S_in);
  220.         S_ans = S_in;
  221.         S_out = 0;
  222.         index_i = 0;
  223.         for(index_i = 0; index_i < 1000000; index_i++)
  224.         {
  225.             S_Box(S_in,S_out);
  226.             S_out &= 0x000000ff;
  227.             if(S_out == S_ans)
  228.             {
  229.                 printf("连续施加S盒变换,变换 %d 次时出现输出等于输入(十进制)\n", index_i+1);
  230.                 break;
  231.             }
  232.             else
  233.                 S_in = S_out;
  234.         }
  235.         if(index_i == 1000000)
  236.             printf("连续施加S盒变换,变换 %d 次也没有出现输出等于输入,应该为无穷多次\n", index_i);
  237.         return 0;
  238.     }
  239.     return 0;
  240. }
  241. int comp_diff(unsigned int A,unsigned int B, int len)//统计两个int型的不同的bit的个数,len为统计的长度
  242. {
  243.     unsigned int C = A^B;
  244.     int index = 0,ans = 0;
  245.     unsigned int con = 0x01;
  246.     for(index = 0; index < len ; index++ )
  247.     {
  248.         if(C&(con << index))
  249.             ans++;
  250.     }
  251.     return ans;
  252. }
  253. void getMessage(int &flag)//读取输入
  254. {
  255.     memset(X,0,sizeof(X));
  256.     int inflag = 1;
  257.     int index = 0;
  258.     while(inflag && index < 4)
  259.     {
  260.         X[index] = 0;
  261.         inflag = fread((void *)(X + index),1,4,fp);
  262.         if(index == 0 && inflag == 0)
  263.             flag = 0;
  264.         index++;
  265.     }
  266.     for(index = 0 ; index < 4; index++)
  267.     {
  268.         X[index] = (X[index]>>24)|((X[index]&0x00ff0000)>>8)|((X[index]&0x0000ff00)<<8)|((X[index]&0x000000ff)<<24);
  269.     }
  270. }
  271. void encrypt()//加密或解密
  272. {
  273.     int index = 0;
  274.     for(index = 0 ; index < RoundNum; index++)
  275.     {
  276.         X[index + 4] = (X[index]^T(X[index+1]^X[index+2]^X[index+3]^key[index]));
  277.     }
  278.     Y[0] = X[35];
  279.     Y[1] = X[34];
  280.     Y[2] = X[33];
  281.     Y[3] = X[32];
  282. }
  283. unsigned int T(unsigned int A)
  284. {
  285.     unsigned int B,C;
  286.     S_Box(A, B);
  287.     C = (B ^ ((B<<2)|(B>>30))^((B<<10)|(B>>22))^((B<<18)|(B>>14))^((B<<24)|(B>>8)));
  288.     return C;
  289. }
  290. void S_Box( unsigned int &A, unsigned int &B)//S box置换
  291. {
  292.     int index = 3;
  293.     unsigned short Temp = 0;
  294.     unsigned int ans = 0;
  295.     B = 0;
  296.     for (index = 3 ; index >= 0; index--)
  297.     {
  298.         Temp = (A>>(index<<3))&0x00ff;
  299.         ans = (unsigned short)S_Box_Table[Temp>>4][Temp&0x0f];
  300.         B|=(ans<<(index<<3));
  301.     }
  302. }
  303. void genKeys(char type)//密钥编排
  304. {
  305.     K[0] = MK[0]^FK[0];
  306.     K[1] = MK[1]^FK[1];
  307.     K[2] = MK[2]^FK[2];
  308.     K[3] = MK[3]^FK[3];
  309.     int index = 0 ;
  310.     for ( index = 0 ; index < RoundNum; index++)
  311.     {
  312.         K[index + 4] = K[index] ^ T2(K[index+1]^K[index+2]^K[index+3]^CK[index]);
  313.         key[index] = K[index + 4];
  314.     }
  315.     if(type == 'd')//解密时,将密钥反序
  316.     {
  317.         unsigned int Temp = 0 ;
  318.         for (index = 0 ; index < RoundNum/2; index++)
  319.         {
  320.             Temp = key[index];
  321.             key[index] = key[31 - index];
  322.             key[31 - index] = Temp;
  323.         }
  324.     }
  325.     // for( index =0 ;index < RoundNum; index++)
  326.         // printf("%0x ", key[index]);
  327.     // printf("\n");
  328. }
  329. unsigned int T2(unsigned int A)
  330. {
  331.     unsigned int B,C;
  332.     S_Box(A, B);
  333.     C = (B ^ ((B<<13)|(B>>19))^((B<<23)|(B>>9)));
  334.     //优先级,||与|是不同的
  335.     return C;
  336. }

SMS4体制的安全性在于S盒的设置,S盒是分组密码中的重要组成部分,是大多数分组密码中唯一的非线性结构,在很大程度上决定分组密码的安全性。通过分析,SMS4加密算法S盒拥有一些较好的安全特性。

小知识之分组加密

分组密码是将明文消息编码表示后的数字(简称明文数字)序列,划分成长度为n的组(可看成长度为n的矢量),每组分别在密钥的控制下变换成等长的输出数字(简称密文数字)序列。