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<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中的封裝庫