SM4密码算法是一个分组算法。数据分组长度为128比特,密钥长度为128 比特。加密算法采用32 轮迭代结构,每轮使用一个轮密钥。

我们在iOS中实现可用data字节的形式,即秘钥Data为16位,加密数据Data需为16的整数倍,这两点很重要。

1、ECB模式

观察第一块,和第三块,皆为明文块0,相同的输入产生相同的输出

ios vlc smb ios vlc smb密码_ci

来看下具体代码

sm4Length:原数据的具体长度

unsigned char *plainChar: 原数据

unsigned char *cipherChar = malloc(sm4Length); 加密后的数据

调用: sm4_crypt_ecb(&ctx, SM4_ENCRYPT, sm4Length, plainChar, cipherChar);
具体实现

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;
     }}

可以看出加密形式,单块加密,比较简单有利于计算,误差不会被传送。但是明文也容易被攻击。

此外,对于原文不足data不足16的整数倍,填充规则是可以自己规定的,同一原文填充规则不同,得到的密文也不同。

在此贴出部分OC处理sm4加密方法

//原数据长度
     NSUInteger plainDataLength = plainData.length;
     int sm4Length = (int)(plainDataLength);
     //申请内存并填充
     unsigned char *plainChar = malloc(sm4Length);
     if (plainChar == NULL) {
         return nil;
     }
     memcpy(plainChar, plainData.bytes, plainDataLength);
     //申请内存
     unsigned char *cipherChar = malloc(sm4Length);
     if (cipherChar == NULL) {
         free(plainChar);
         return nil;
     }
     //加密
     sm4_context ctx;
     sm4_setkey_enc(&ctx, (unsigned char*)keyData.bytes);
     sm4_crypt_ecb(&ctx, SM4_ENCRYPT, sm4Length, plainChar, cipherChar);
     //处理加密的输出数组为Data
     NSData *chipherData = [[NSData alloc] initWithBytes:cipherChar length:sm4Length];
     //释放内存
     free(plainChar);
     free(cipherChar);
     return chipherData;

2、CBC模式

ios vlc smb ios vlc smb密码_ios vlc smb_02

CBC(密文分组链接方式),它的实现机制使加密的各段数据之间有了联系。

也是按照data 16位来分组,第一组数据与初始化向量IV异或后的结果进行加密,密得到第一组密文C1(初始化向量I为全零),第二组数据与第一组的加密结果C1异或以后的结果进行加密,得到第二组密文C2...... 最后C1C2C3......Cn即为加密结果。

此种方法安全性高,但是不利于并行计算,有误差传递,需要初始化向量IV

展示部分代码

调用

sm4_crypt_cbc(&ctx, SM4_ENCRYPT, sm4Length, sm4IVChar, plainChar, cipherChar);
实现
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 ); //更改iv            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;
         }
     }
 }

其中

 

output[i] = (unsigned char)( input[i] ^ iv[i] ); //异或运算
            sm4_one_round( ctx->sk, output, output );
             memcpy( iv, output, 16 ); //更改iv这三行代码 我们也能读出 主要加密链式模式
贴出OC 调用CBC加密部分代码
//原数据长度
     NSUInteger plainDataLength = plainData.length;
     
     //获取sm4长度,必须是16的整数倍(16个字节,128bit)
     NSUInteger p = SM4_BLOCK_SIZE - plainDataLength % SM4_BLOCK_SIZE;
     int sm4Length = (int)(plainDataLength + p);
     //申请内存
     unsigned char *plainChar = malloc(sm4Length);
     if (plainChar == NULL) {
         return nil;
     }
     memcpy(plainChar, plainData.bytes, plainDataLength);
     
     //补充位填充扩展了多少位,解密时用到
     for (int i=0; i<p; i++) {
         plainChar[plainDataLength + i] = p;
     }
     
     //加密输出数组
     unsigned char *cipherChar = malloc(sm4Length);
     if (cipherChar == NULL) {
         free(plainChar);
         return nil;
     }
     
     //复制IV值
     unsigned char sm4IVChar[SM4_BLOCK_SIZE];
     memcpy(sm4IVChar, IV.bytes, SM4_BLOCK_SIZE);
     
     //执行加密
     sm4_context ctx;
     sm4_setkey_enc(&ctx, (unsigned char *)keyData.bytes);
     sm4_crypt_cbc(&ctx, SM4_ENCRYPT, sm4Length, sm4IVChar, plainChar, cipherChar);
     
     //处理加密输出数组为Data
     NSData *cipherData = [[NSData alloc] initWithBytes:cipherChar length:sm4Length];
     
     //释放内存
     free(plainChar);
     free(cipherChar);
     
     return cipherData;


  

稍微会继续分析sm2加解密、加签延签流程

与分享sm2 sm3 sm4 在iOS中的封装库