SM4加密算法属于对称加密算法,2012年3月,国家密码管理局正式公布了包含SM4分组密码算法在内的《祖冲之序列密码算法》等6项密码行业标准。与DES和AES算法类似,SM4算法是一种分组密码算法。其分组长度为128bit,密钥长度也为128bit。加密算法与密钥扩展算法均采用32轮非线性迭代结构,以字(32位)为单位进行加密运算,每一次迭代运算均为一轮变换函数F。SM4算法加/解密算法的结构相同,只是使用轮密钥相反,其中解密轮密钥是加密轮密钥的逆序。
SMhttps://n0va-scy.github.io/2020/03/14/sm4%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/
4算法结构图:(图片来源网上)
https://n0va-scy.github.io/2020/03/14/sm4%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/
参数介绍
1、字节:由8位2进制数表示 ,字:由32位2进制数表示;
2、S盒:固定的8bit输入、输出转换数组;
3、加密密钥长度为128bit,表示为MK=(MK0,MK1,MK2,MK3),其中MKi为字。轮密钥表示为rki(i=0,1,2,3……31)为字。FK = (FK0,FK1,FK2,FK3)为系统 参数,CK = (CK0,CK1,CK2,……,CK31)为固定参数,都为字。
加密
加密原理这里就不讲了,网上有不少详细的文章,下面直接开始算法的实现。
在开始加密算法之前,先介绍几个宏定义:
1
|
//将字符型数组b的第i到第i+3位的二进制拼接成一个4*8=32bit的整数,存入n中
|
密钥调试算法
先建立一个结构体来保存上下文信息,即加密模式和各轮子密钥:(这个结构体我是定义在sm4.h头文件中)
1
|
typedef struct
|
首先需要设置密钥,调用sm4_setkey_enc(&ctx,key);
函数,这个函数会设置mode为加密,并调用sm4_setkey(ctx->sk,key);
函数来完成密钥的操作
1
|
void sm4_setkey_enc(sm4_context *ctx,unsigned char key[16]){
|
对于第i轮的密钥SK[i],其是由k[i]和对k[i+1]^k[i+2]^k[i+3]^CK[i]的复合变换T‘异或得到的:
SK[i] = k[i+4] = k[i]^sm4CaliRk(k[i+1]^k[i+2]^k[i+3]^CK[i]);
函数sm4CaliRk
就是变换T’,先进行Sbox的非线性替换,然后进行线性变换,线性变换L为:rk = bb^(ROTL(bb, 13))^(ROTL(bb, 23));
1
|
static unsigned long sm4CaliRk(unsigned long ka){ //复合变换T
|
至此,密钥就已经生成了
加密过程
调用函数void sm4_crypt_ecb( sm4_context *ctx, int mode, int length, unsigned char *input, unsigned char *output)
对密文input进行电码本模式加密(ECB),加密的核心是调用了static void sm4_one_round( unsigned long sk[32], unsigned char input[16], unsigned char output[16] )
函数对第一块密文进行加密
1
|
void sm4_crypt_ecb( sm4_context *ctx,
|
sm4_one_round()函数中,先将128位的输入input转为四个32位的整数,放入ulbuf[4]中,然后迭代地调用函数static unsigned long sm4F(unsigned long x0, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long rk)
进行32轮加密,第一轮加密都需要使用之前的128位的结果ulbuf[i], ulbuf[i+1], ulbuf[i+2], ulbuf[i+3]和该轮的密钥 sk[i],产生出该轮的密文ulbuf[i+4],最后的密文存储在ulbuf[35]~ulbuf[32]中,转换为字符数组形式放入output 中。
1
|
//一轮加密
|
sm4Lt()是一个合成变换,由非线性变换t和线性变换L复合而成:首先将输入的整数 ka 转换为8比特一个的字符,然后使用S盒进行线性变换,再将变换结果转为32比特的整数,最后对得到的32位整数bb进行线性变换:c =bb^(ROTL(bb, 2))^(ROTL(bb, 10))^(ROTL(bb, 18))^(ROTL(bb, 24));
1
|
static unsigned long sm4Lt(unsigned long ka)
|
整个加密过程就结束了,不过上面提到的是ECB的加密模式:又称电子密码本模式:Electronic codebook,是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。这种加密不能很好地隐藏数据模式。
SM4常见的也有另一种加密模式:CBC:密码分组链接(CBC,Cipher-block chaining)模式,由IBM于1976年发明,每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。同时,为了保证每条消息的唯一性,在第一个块中需要使用初始化向量IV。
1
|
//CBC模式加解密
|
解密过程
解密前,首先要通过void sm4_setkey_dec( sm4_context *ctx, unsigned char key[16] )函数设定解密时使用的key,这个函数还会将密钥的顺序倒置,然后调用sm4_crypt_ecb()即可解密。
实际上,SM4的解密变换与加密变换结构相同,不同的仅仅是轮密钥的使用顺序相反。
完整的代码
sm4.h文件
1
|
/** |