iOS 實(shí)現(xiàn)對(duì)稱加密多種填充方式(ANSIX923井誉、ISO10126、Zero)

前言

????前段時(shí)間調(diào)研了iOS有關(guān)加密的框架CommonCrypto.在查閱資料的過程中整胃,有一篇《iOS加密解密:AES,DES,3DES,BLOWFISH(含有多種模式和算法說明)》
的文章颗圣。
在文章中作者對(duì)需要加密的數(shù)據(jù)實(shí)現(xiàn)PKCS7Padding填充,并在調(diào)用CCCryptorCreateWithMode()函數(shù)時(shí)將padding參數(shù)賦值為ccNoPadding(不填充)屁使。在作者的基礎(chǔ)上我增添了ANSIX923,ISO10126以及Zero填充方式,以及解密之后的去除數(shù)據(jù)的填充在岂。

????在對(duì)稱加密中,可以概分為兩種模式加密蛮寂,流加密以及塊加密蔽午,當(dāng)我們使用塊加密(也就是分組加密)的時(shí)候,例如AES酬蹋、DES及老,每次是對(duì)固定大小的分組數(shù)據(jù)進(jìn)行處理。但是大多數(shù)需要加密的數(shù)據(jù)并不是固定大小的倍數(shù)長度范抓。例如AES數(shù)據(jù)塊為128位骄恶,也就是16字節(jié)長度,而需要加密的長度可能為15尉咕、26等等叠蝇。為了解決這個(gè)問題璃岳,我們就需要對(duì)數(shù)據(jù)進(jìn)行填補(bǔ)操作年缎,將數(shù)據(jù)補(bǔ)齊至對(duì)應(yīng)塊長度。

補(bǔ)碼原理

接下來呢講一下關(guān)于數(shù)據(jù)填充ANSIX923铃慷、ISO10126单芜、PKCS7以及Zero具體的補(bǔ)碼原理。

注犁柜,補(bǔ)碼原理來自于文章Padding (cryptography)洲鸠,讀中文的小伙伴們可以查看這篇 關(guān)于PKCS5Padding與PKCS7Padding的區(qū)別.
  • ANSIX923 填充方式

    ANSIX923 在填充時(shí)首先獲取需要填充的字節(jié)長度 = (塊長度 - (數(shù)據(jù)長度 % 塊長度)), 在填充字節(jié)序列中最后一個(gè)字節(jié)填充為需要填充的字節(jié)長度值, 填充字節(jié)中其余字節(jié)均填充數(shù)字零.
    例:

    假定塊長度為8 ,數(shù)據(jù)長度為 10,則填充字節(jié)數(shù)等于 6馋缅,數(shù)據(jù)等于 FF FF FF FF FF FF FF FF FF DD:
    數(shù)據(jù): FF FF FF FF FF FF FF FF FF
    X923 填充后: FF FF FF FF FF FF FF FF | FF DD 00 00 00 00 00 06
    
  • ISO10126 填充方式

    ISO10126 在填充時(shí)首先獲取需要填充的字節(jié)長度 = (塊長度 - (數(shù)據(jù)長度 % 塊長度)), 在填充字節(jié)序列中最后一個(gè)字節(jié)填充為需要填充的字節(jié)長度值, 填充字節(jié)中其余字節(jié)均填充隨機(jī)數(shù)值.
    例:

    假定塊長度為 16扒腕,數(shù)據(jù)長度為 9,則填充字節(jié)數(shù)等于 7,數(shù)據(jù)等于 FF FF FF FF FF FF FF FF FF :
    
    | FF FF FF FF FF FF FF FF FF 73 68 C4 81 A6 23 07 |
    
  • PKCS7 填充方式

    PKCS7 在填充時(shí)首先獲取需要填充的字節(jié)長度 = (塊長度 - (數(shù)據(jù)長度 % 塊長度)), 在填充字節(jié)序列中所有字節(jié)填充為需要填充的字節(jié)長度值
    例:

    假定塊長度為 8萤悴,數(shù)據(jù)長度為 3,則填充字節(jié)數(shù)等于 5瘾腰,數(shù)據(jù)等于 FF FF FF :
    | FF FF FF 05 05 05 05 05 |
    
  • Zero 填充方式

    Zero 在填充時(shí)首先獲取需要填充的字節(jié)長度 = (塊長度 - (數(shù)據(jù)長度 % 塊長度)), 在填充字節(jié)序列中所有字節(jié)填充為0x00,零填充在數(shù)據(jù)最后字節(jié)為零的時(shí)候可能不可逆
    例:

    假定塊長度為 8覆履,數(shù)據(jù)長度為 2,則填充字節(jié)數(shù)等于 6蹋盆,數(shù)據(jù)等于 FF FF  :
    | FF FF 00 00 00 00 00 00 |
    

特定的费薄,為了使算法可以逆向去除多余的填充字符,所以當(dāng)數(shù)據(jù)長度恰好等于塊長度的時(shí)候栖雾,需要補(bǔ)足塊長度的字節(jié).例如塊長度為8楞抡,數(shù)據(jù)長度為8,則填充字節(jié)數(shù)等于8.

代碼部分

為了可以完成自己的填充算法方便調(diào)用析藕,并同時(shí)省去PKCS7部分的填充操作召廷,先實(shí)現(xiàn)一個(gè)枚舉并保持系統(tǒng)枚舉CCPadding的枚舉值也在其中,代碼實(shí)例如下

typedef enum : NSUInteger {
    CcCryptorNoPadding = 0, //No Padding to source Data
    CcCryptorPKCS7Padding = 1, // PKCS_7 | Each byte fills in the length of the sequence of the bytes .  ***This Padding Mode  use the system method.***
    CcCryptorZeroPadding = 2,   // 0x00 Padding |  Each byte fills 0x00
    CcCryptorANSIX923,     // The last byte fills the length of the byte sequence, and the               remaining bytes are filled with 0x00.
    CcCryptorISO10126      // The last byte fills the length of the byte sequence and  the remaining bytes fill the random data.
}CcCryptorPadding;

并在調(diào)用CCCryptorCreateWithMode函數(shù)之前账胧,判斷填充模式CCPadding paddingMode = ((padding == ccPKCS7Padding) ? ccPKCS7Padding:ccNoPadding) ;,使得我們可以正常使用系統(tǒng)的填充模式之外使用其他自定義填充模式柱恤。

下面是iOS部分如何實(shí)現(xiàn)對(duì)數(shù)據(jù)進(jìn)行填充,此函數(shù)需要在CCCryptorCreateWithMode函數(shù)之前對(duì)數(shù)據(jù)進(jìn)行處理.

// Fill in the bytes that need to be encrypted.
static NSData * bitPadding(CCOperation operation, CCAlgorithm algorithm ,CcCryptorPadding padding, NSData *data)
{
    //當(dāng)模式為PKCS7填充時(shí)找爱,我們選擇使用系統(tǒng)PKCS7填充梗顺,返回原始數(shù)據(jù),避免二次填充
    if (padding == CcCryptorPKCS7Padding) {
        return  data;
    }
    // 當(dāng)前處于加密模式车摄,并且不是流加密的情況下.
    if (operation == kCCEncrypt && (algorithm != CcCryptoAlgorithmRC4)  ) {
        //獲取一份可操作的數(shù)據(jù)對(duì)象
        NSMutableData *sourceData = data.mutableCopy;
        //設(shè)置默認(rèn)塊大小為8.
        int blockSize = 8;
        switch (algorithm) {
            // 當(dāng)AES加密時(shí)寺谤,塊大小為16.
            case kCCAlgorithmAES:
                blockSize = kCCBlockSizeAES128;
                break;
            case kCCAlgorithmDES:
            case kCCAlgorithm3DES:
            case kCCAlgorithmCAST:
            case kCCAlgorithmBlowfish:
            case kCCAlgorithmRC2:
            default:
                blockSize = 8;
                break;
        }
        // 根據(jù)選定填充模式實(shí)現(xiàn)不同填充
        switch (padding) {
            // 零填充
            case CcCryptorZeroPadding:
            {
                int pad = 0x00;
                int diff =   blockSize - (sourceData.length % blockSize);
                for (int i = 0; i < diff; i++) {
                    [sourceData appendBytes:&pad length:1];
                }
            }
                break;
            // ANSIX923
            case CcCryptorANSIX923:
            {
                int pad = 0x00;
                int diff =   blockSize - (sourceData.length % blockSize);
                for (int i = 0; i < diff - 1; i++) {
                    [sourceData appendBytes:&pad length:1];
                }
                [sourceData appendBytes:&diff length:1];
            }
                break;
            //ISO10126
            case CcCryptorISO10126:
            {
                int diff = blockSize - (sourceData.length % blockSize);
                for (int i = 0; i < diff - 1; i++) {
                    int pad  = arc4random() % 254 + 1;
                    [sourceData appendBytes:&pad length:1];
                }
                [sourceData appendBytes:&diff length:1];
            }
                break;
            default:
                break;
        }
        return sourceData;
    }
    return data;
}

當(dāng)我們對(duì)數(shù)據(jù)解密過后,如果不去除填充字節(jié)吮播,會(huì)發(fā)現(xiàn)在解密后的字符串末尾發(fā)現(xiàn)填充字節(jié)造成的字符
因此变屁,我們需要在解密過后對(duì)數(shù)據(jù)進(jìn)行去除可能存在的填充字節(jié)的處理。代碼示例如下:

static NSData * removeBitPadding(CCOperation operation, CCAlgorithm algorithm ,CcCryptorPadding padding, NSData *sourceData)
{
    //若為PKCS7意狠,則交由系統(tǒng)函數(shù)處理.
    if (padding == CcCryptorPKCS7Padding) {
        return sourceData;
    }
    //若為解密粟关,并且為塊加密
    if (operation == kCCDecrypt && (algorithm != CcCryptoAlgorithmRC4) ) {
        // 設(shè)置需要非填充字節(jié)長度為0
        int correctLength = 0;
         // 設(shè)置塊長度默認(rèn)值為8
        int blockSize = 8;
        判斷塊長度
        switch (algorithm) {
            case kCCAlgorithmAES:
                blockSize = kCCBlockSizeAES128;
                break;
            case kCCAlgorithmDES:
            case kCCAlgorithm3DES:
            case kCCAlgorithmCAST:
            case kCCAlgorithmBlowfish:
            default:
                blockSize = 8;
                break;
        }
        // 將data -> byte 
        Byte *testByte = (Byte *)[sourceData bytes];
        // 獲取最后一位字節(jié).
        char end = testByte[sourceData.length - 1];
        // 根據(jù)選定填充模式不同去除可能存在的填充字符
        if (padding == CcCryptorZeroPadding && end == 0) {
            for (int i = (short)sourceData.length - 1; i > 0 ; i--) {
                //若不相等為零字節(jié),則停止
                if (testByte[i] != end) {
                    // 非填充字節(jié)的長度為i
                    correctLength = i + 1;
                    break;
                }
            }
        }

        else if ((padding == CcCryptorANSIX923 || padding == CcCryptorISO10126) && (end > 0 && end < blockSize + 1)){
            if (padding == CcCryptorISO10126 || ( testByte[sourceData.length - 2] == 0 &&  testByte[sourceData.length - end] == 0)) {
                  //若填充模式為ANSIX923 或者ISO10126 則根據(jù)最后一位字節(jié)的大小判斷填充字節(jié)的長度以及非填充字節(jié)的長度
                correctLength = (short)sourceData.length - end;
            }
        }
        // 直接根據(jù)獲取的非填充字節(jié)的長度以及Byte 生成新的僅有正確字節(jié)的data數(shù)據(jù).
        NSData *data = [NSData dataWithBytes:testByte length:correctLength];
        return data;
        
    }
    return sourceData;
}

想看源碼的可以去Github.

有小伙伴們對(duì)CommonCrypto环戈、對(duì)稱加密感興趣的可以看看這篇文章iOS 關(guān)于CommonCrypto加密 ②(CommonCryptor AES|DES|3DES|CAST|BlowFish)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末闷板,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子院塞,更是在濱河造成了極大的恐慌遮晚,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拦止,死亡現(xiàn)場離奇詭異县遣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)汹族,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門萧求,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人顶瞒,你說我怎么就攤上這事夸政。” “怎么了搁拙?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵秒梳,是天一觀的道長法绵。 經(jīng)常有香客問我,道長酪碘,這世上最難降的妖魔是什么朋譬? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮兴垦,結(jié)果婚禮上徙赢,老公的妹妹穿的比我還像新娘。我一直安慰自己探越,他們只是感情好狡赐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钦幔,像睡著了一般枕屉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鲤氢,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天搀擂,我揣著相機(jī)與錄音,去河邊找鬼卷玉。 笑死哨颂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的相种。 我是一名探鬼主播威恼,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼寝并!你這毒婦竟也來了箫措?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤食茎,失蹤者是張志新(化名)和其女友劉穎蒂破,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體别渔,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年惧互,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哎媚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喊儡,死狀恐怖拨与,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情艾猜,我是刑警寧澤买喧,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布捻悯,位于F島的核電站,受9級(jí)特大地震影響淤毛,放射性物質(zhì)發(fā)生泄漏今缚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一低淡、第九天 我趴在偏房一處隱蔽的房頂上張望姓言。 院中可真熱鬧,春花似錦蔗蹋、人聲如沸何荚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽餐塘。三九已至,卻和暖如春皂吮,著一層夾襖步出監(jiān)牢的瞬間唠倦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國打工涮较, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留稠鼻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓狂票,卻偏偏與公主長得像候齿,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闺属,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容