國密sm4 ECB、CEC模式探究與在iOS中的應用

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

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章