SM4算法

一、概述

国密算法定义:即国家密码局认定的国产密码算法。

通过定义我们可以知道,国密算法有两个要素:

1、国家密码局认定

  在国家密码局官网上,可以看到由其发布的标准规范。

2、密码算法

首先知道什么是密码,密码就是将正常的信息加密后变为无法正常识别的编码,可以认为是一种混淆技术。

将明文数据通过密码算法变成密文后,有三个优点:

(1)、数据保密,混淆后的密文一般无直接意义。

(2)、数据完整,混淆后的密文缺失无法正常恢复。

(3)、身份验证,数据加解密或者对照,都要有密钥或算法的信息,一般可以认为是自己人。

  严格意义上的加密是必须保证能恢复明文信息的,但是我们平时说的一些加密,又要要求不能恢复原来的信息,如账号密码里的密码,一般是要求不允许恢复原文的。这时候就出现了一个问题,加密后还能不能恢复成加密前的样子?技术上能恢复的叫加密算法,不能恢复的叫哈希算法能恢复明文的算法里又分为两种:对称加密和非对称加密

  对称加密:加密时使用的密钥和解密时使用的密钥为同一个

  非对称加密:加解密时使用的密钥是一对,公开密钥和私有密钥,公开密钥用于数据加密,私有密钥用于数据解密

哈希算法:通过算法对明文数据进行混淆,同一个明文在同一个哈希算法下混淆结果一致

具体分类可见下表

01.png

下图是实际开发中遇到的要求:

02.png

为什么用国密算法

(1)、国密算法算法好,速度快。

(2)、支持国产。

(3)、有些企业或部门要求使用,从最近的情况来看,还是自家的比较安全。

  与DES和AES算法相似,国密SM4算法是一种分组加密算法。SM4分组密码(block cipher)算法是一种迭代分组密码算法,由加解密算法密钥扩展算法组成。

SM4是一种Feistel结构的分组密码算法,其分组长度和密钥长度均为128bits加密算法和密钥扩展算法迭代轮数均为32轮。SM4加解密过程的算法相同但是轮密钥的使用顺序相反

SM4密码算法使用模2加和循环移位作为基本运算。

密钥扩展算法:SM4算法使用128位的加密密钥,并采用32轮迭代加密结构,每一轮加密使用一个32位的轮密钥,总共使用32个轮密钥。因此需要使用密钥扩展算法,从加密密钥中产生32个轮密钥。

SM4算法使用128位的加密密钥,而UUID是一个128位的二进制数,故我们可以使用UUID来当作SM4算法的密钥,注意:需要将UUID的横杠-替换成空字符串

二、SM4加解密流程

SM4算法的加密大致流程如下:

03.png

密钥:加密密钥的长度为128比特,表示为MK = (MK0, MK1, MK2, MK3),其中MKi为32位,

轮密钥表示为(rk0, rk1, ……, rk31),其中rki为32位。

轮函数F:假设输入为(X0, X1, X2, X3),X为32位,则轮函数F为:F=(X0, X1, X2, X3, rk) = X0 ⊕ T(X1 ⊕ X2 ⊕ X3 ⊕ rk)

合成置换:T函数是一个可逆变换,由一个非线性变换r和线性变换L复合而成的,即T( )=L(r( ))

非线性变换有四个并行的S盒构成的,设输入为A=(a0, a1, a2, a3),输出为B=(b0, b1, b2, b3),其中ai和bi为8位。每个S盒的输入都是一个8位的字节,将这8位的前四位对应的16进制数作为行编号,后四位对应的16进制数作为列编号,然后用相应位置中的字节代替输入的字节。下图为S盒置换表:

04.png

 05.png

线性变换L:线形变换的输入就是S盒的输出,即C=L(B)=B ⊕ (B<<<2) ⊕ (B<<<10) ⊕ (B<<<18) ⊕ (B<<<24),线性变换的输入和输出都是32位的。

经过了32轮的迭代运算后,最后再进行一次反序变换即可得到加密的密文,即密文C=(Y0, Y1, Y2, Y3)=R(X32. X33, X34, X35)=(X35, X34, X33, X32)。

SM4算法的解密流程和加密流程一致,只不过轮密钥的使用顺序变成了(rk31, rk30, ……, rk0)

三、密钥扩展算法

密钥参量:轮密钥由加密密钥生成。FK=(FK0, FK1, FK2, FK3)为系统参数,以及固定参数CK=(CK0, CK1, ……,  CK31),其中FKi和CKi均为32位并用于密钥扩展算法。

系统参数FK的具体取值如下:

FK0=(A3B1BAC6), FK1=(56AA3350), FK2=(677D9197), FK3=(B27022DC)

固定参数CK的具体取值如下:

06.png

密钥扩展方法:设(K0, K1, K2, K3)=(MK0⊕FK0, MK1⊕FK1, MK2⊕FK2, MK3⊕FK3)

则rki=Ki+4=Ki⊕T‘(Ki+1⊕Ki+2⊕Ki+3⊕CKi)

其中T’()是将原来的T()中的线形变换L()替换成L'(B)=B⊕(B<<<13)⊕(B<<<23)

四、C++代码

代码如下:

#include <string.h>
#include <stdio.h>
#include <time.h>
/*--------------------------------------------------------------------------------------------------------------*/
#define SM4_ENCRYPT 1
#define SM4_DECRYPT 0
/*--------------------------------------------------------------------------------------------------------------*/
/**
 * \brief SM4 context structure
 */
typedef struct {
int mode; /*!< encrypt/decrypt */
unsigned long sk[32]; /*!< SM4 subkeys */
}
sm4_context;
/*--------------------------------------------------------------------------------------------------------------*/
/**
 * \brief SM4 key schedule (128-bit, encryption)
 *
 * \param ctx SM4 context to be initialized
 * \param key 16-byte secret key
 */
void sm4_setkey_enc(sm4_context *ctx, unsigned char key[16]);
/*--------------------------------------------------------------------------------------------------------------*/
/**
 * \brief SM4 key schedule (128-bit, decryption)
 *
 * \param ctx SM4 context to be initialized
 * \param key 16-byte secret key
 */
void sm4_setkey_dec(sm4_context *ctx, unsigned char key[16]);
/*--------------------------------------------------------------------------------------------------------------*/
/**
 * \brief SM4-ECB block encryption/decryption
 * \param ctx SM4 context
 * \param mode SM4_ENCRYPT or SM4_DECRYPT
 * \param length length of the input data
 * \param input input block
 * \param output output block
 */
void sm4_crypt_ecb(sm4_context *ctx,
                   int mode, int length,
                   unsigned char *input,
                   unsigned char *output);
/*--------------------------------------------------------------------------------------------------------------*/
/**
 * \brief SM4-CBC buffer encryption/decryption
 * \param ctx SM4 context
 * \param mode SM4_ENCRYPT or SM4_DECRYPT
 * \param length length of the input data
 * \param iv initialization vector (updated after use)
 * \param input buffer holding the input data
 * \param output buffer holding the output data
 */
void sm4_crypt_cbc(sm4_context *ctx,
                   int mode,
                   int length,
                   unsigned char iv[16],
                   unsigned char *input,
                   unsigned char *output);
/*--------------------------------------------------------------------------------------------------------------*/
/*
* 32-bit integer manipulation macros (big endian)
*/
#ifndef GET_ULONG_BE
#define GET_ULONG_BE(n,b,i) \
{ \
(n) = ( (unsigned long) (b)[(i) ] << 24 ) \
| ( (unsigned long) (b)[(i) + 1] << 16 ) \
| ( (unsigned long) (b)[(i) + 2] << 8 ) \
| ( (unsigned long) (b)[(i) + 3] ); \
}
#endif
/*--------------------------------------------------------------------------------------------------------------*/
#ifndef PUT_ULONG_BE
#define PUT_ULONG_BE(n,b,i) \
{ \
(b)[(i) ] = (unsigned char) ( (n) >> 24 ); \
(b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \
(b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \
(b)[(i) + 3] = (unsigned char) ( (n) ); \
}
#endif
/*--------------------------------------------------------------------------------------------------------------*/
/**rotate shift left marco definition
 *
 */
#define SHL(x,n) (((x) & 0xFFFFFFFF) << n)
#define ROTL(x,n) (SHL((x),n) | ((x) >> (32 - n)))
#define SWAP(a,b) { unsigned long t = a; a = b; b = t; t = 0; }
/*--------------------------------------------------------------------------------------------------------------*/
/*
 Expanded SM4 S-boxes
 Sbox table: 8bits input convert to 8 bits output
*/
static const unsigned char SboxTable[16][16] = {
{ 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05 },
{ 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99 },
{ 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62 },
{ 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6 },
{ 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8 },
{ 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35 },
{ 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87 },
{ 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 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, 0x0d, 0x53, 0x4e, 0x6f },
{ 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51 },
{ 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8 },
{ 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0 },
{ 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84 },
{ 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48 }
};
/*--------------------------------------------------------------------------------------------------------------*/
/* System parameter */
static const unsigned long FK[4] =
{ 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc };
/*--------------------------------------------------------------------------------------------------------------*/
/* fixed parameter */
static const unsigned long CK[32] = {
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
};
/*--------------------------------------------------------------------------------------------------------------*/
/*
* private function:
* look up in SboxTable and get the related value.
* args: [in] inch: 0x00~0xFF (8 bits unsigned value).
*/
static unsigned char sm4Sbox(unsigned char inch) {
unsigned char *pTable = (unsigned char *)SboxTable;
unsigned char retVal = (unsigned char)(pTable[inch]);
return retVal;
}
/*--------------------------------------------------------------------------------------------------------------*/
/*
* private F(Lt) function:
* "T algorithm" == "L algorithm" + "t algorithm".
* args: [in] a: a is a 32 bits unsigned value;
* return: c: c is calculated with line algorithm "L" and nonline algorithm "t"
*/
static unsigned long sm4Lt(unsigned long ka) {
unsigned long bb = 0;
unsigned long c = 0;
unsigned char a[4];
unsigned char b[4];
PUT_ULONG_BE(ka, a, 0)
b[0] = sm4Sbox(a[0]);
b[1] = sm4Sbox(a[1]);
b[2] = sm4Sbox(a[2]);
b[3] = sm4Sbox(a[3]);
GET_ULONG_BE(bb, b, 0)
c = bb ^ (ROTL(bb, 2)) ^ (ROTL(bb, 10)) ^ (ROTL(bb, 18)) ^ (ROTL(bb, 24));
return c;
}
/*--------------------------------------------------------------------------------------------------------------*/
/*
* private F function:
* Calculating and getting encryption/decryption contents.
* args: [in] x0: original contents;
* args: [in] x1: original contents;
* args: [in] x2: original contents;
* args: [in] x3: original contents;* args: [in] rk: encryption/decryption key;
* return the contents of encryption/decryption contents.
*/
static unsigned long sm4F(unsigned long x0, unsigned long x1, unsigned long x2, unsigned long x3,
                          unsigned long rk) {
return (x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk));
}
/*--------------------------------------------------------------------------------------------------------------*/
/* private function:
* Calculating round encryption key.
* args: [in] a: a is a 32 bits unsigned value;
* return: sk[i]: i{0,1,2,3,...31}.
*/
static unsigned long sm4CalciRK(unsigned long ka) {
unsigned long bb = 0;
unsigned long rk = 0;
unsigned char a[4];
unsigned char b[4];
PUT_ULONG_BE(ka, a, 0)
b[0] = sm4Sbox(a[0]);
b[1] = sm4Sbox(a[1]);
b[2] = sm4Sbox(a[2]);
b[3] = sm4Sbox(a[3]);
GET_ULONG_BE(bb, b, 0)
rk = bb ^ (ROTL(bb, 13)) ^ (ROTL(bb, 23));
return rk;
}
/*--------------------------------------------------------------------------------------------------------------*/
static void sm4_setkey(unsigned long SK[32], unsigned char key[16]) {
unsigned long MK[4];
unsigned long k[36];
unsigned long i = 0;
GET_ULONG_BE(MK[0], key, 0);
GET_ULONG_BE(MK[1], key, 4);
GET_ULONG_BE(MK[2], key, 8);
GET_ULONG_BE(MK[3], key, 12);
k[0] = MK[0] ^ FK[0];
k[1] = MK[1] ^ FK[1];
k[2] = MK[2] ^ FK[2];
k[3] = MK[3] ^ FK[3];
for (; i < 32; i++) {
k[i + 4] = k[i] ^ (sm4CalciRK(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ CK[i]));
SK[i] = k[i + 4];
}
}
/*--------------------------------------------------------------------------------------------------------------*/
/*
* SM4 standard one round processing
*
*/
static void sm4_one_round(unsigned long sk[32],
                          unsigned char input[16],
                          unsigned char output[16]) {
unsigned long i = 0;
unsigned long ulbuf[36];
memset(ulbuf, 0, sizeof(ulbuf));
GET_ULONG_BE(ulbuf[0], input, 0)
GET_ULONG_BE(ulbuf[1], input, 4)
GET_ULONG_BE(ulbuf[2], input, 8)
GET_ULONG_BE(ulbuf[3], input, 12)
while (i < 32) {
ulbuf[i + 4] = sm4F(ulbuf[i], ulbuf[i + 1], ulbuf[i + 2], ulbuf[i + 3], sk[i]);
i++;
}
PUT_ULONG_BE(ulbuf[35], output, 0);
PUT_ULONG_BE(ulbuf[34], output, 4);
PUT_ULONG_BE(ulbuf[33], output, 8);
PUT_ULONG_BE(ulbuf[32], output, 12);
}
/*--------------------------------------------------------------------------------------------------------------*/
/*
* SM4 key schedule (128-bit, encryption)
*/
void sm4_setkey_enc(sm4_context *ctx, unsigned char key[16]) {
ctx->mode = SM4_ENCRYPT;
sm4_setkey(ctx->sk, key);
}
/*--------------------------------------------------------------------------------------------------------------*//*
* SM4 key schedule (128-bit, decryption)
*/
void sm4_setkey_dec(sm4_context *ctx, unsigned char key[16]) {
int i;
ctx->mode = SM4_ENCRYPT;
sm4_setkey(ctx->sk, key);
for (i = 0; i < 16; i++) {
SWAP(ctx->sk[i], ctx->sk[31 - i]);
}
}
/*--------------------------------------------------------------------------------------------------------------*/
/*
* SM4-ECB block encryption/decryption
*/
void sm4_crypt_ecb(sm4_context *ctx,
                   int mode,
                   int length,
                   unsigned char *input,
                   unsigned char *output) {
while (length > 0) {
sm4_one_round(ctx->sk, input, output);
input += 16;
output += 16;
length -= 16;
}
}
/*--------------------------------------------------------------------------------------------------------------*/
/*
* SM4-CBC buffer encryption/decryption
*/
void sm4_crypt_cbc(sm4_context *ctx,
                   int mode,
                   int length,
                   unsigned char iv[16],
                   unsigned char *input,
                   unsigned char *output) {
int i;
unsigned char temp[16];
if (mode == SM4_ENCRYPT) {
while (length > 0) {
for (i = 0; i < 16; i++)
output[i] = (unsigned char)(input[i] ^ iv[i]);
sm4_one_round(ctx->sk, output, output);
memcpy(iv, output, 16);
input += 16;
output += 16;
length -= 16;
}
} else { /* SM4_DECRYPT */
while (length > 0) {
memcpy(temp, input, 16);
sm4_one_round(ctx->sk, input, output);
for (i = 0; i < 16; i++)
output[i] = (unsigned char)(output[i] ^ iv[i]);
memcpy(iv, temp, 16);
input += 16;
output += 16;
length -= 16;
}
}
}
/*--------------------------------------------------------------------------------------------------------------*/
int main() {
unsigned char key[16] = { 1, 35, 69, 103, 137, 171, 205, 239, 254, 220, 186, 152, 118, 84, 50, 16};
unsigned char input[16] = {0xE5,0xE4,0x54, 0xFE, 0xD8, 0xB2, 0xB9, 0xF5, 0xC2, 0xD9, 0x32, 0xAE, 0xC3, 0xF4, 0xDA, 0xF1 }; 
//unsigned char input[16] = { 55, 49, 51, 99, 98, 52, 99, 52, 49, 102, 100, 97, 53, 99, 57, 50 };
unsigned char output[16];
sm4_context ctx;
unsigned long i;
sm4_setkey_dec(&ctx, key);
sm4_crypt_ecb(&ctx, 0, 16, input, output);
for (i = 0; i < 16; i++)
printf("%c", output[i]);
printf("\n");
return 0;
}


打赏

本篇文章链接 地址:https://wmzos.com/?id=160

相关阅读

添加新评论