SM4密码算法是一个分组算法。数据分组长度为128比特,密钥长度为128 比特。加密算法采用32 轮迭代结构,每轮使用一个轮密钥。
我们在iOS中实现可用data字节的形式,即秘钥Data为16位,加密数据Data需为16的整数倍,这两点很重要。
1、ECB模式
观察第一块,和第三块,皆为明文块0,相同的输入产生相同的输出
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模式
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
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中的封装库