iOS 实现对称加密多种填充方式(ANSIX923、ISO10126、Zero)

前言

    前段时间调研了iOS有关加密的框架CommonCrypto.在查阅资料的过程中,有一篇《iOS加密解密:AES,DES,3DES,BLOWFISH(含有多种模式和算法说明)》
的文章。
在文章中作者对需要加密的数据实现PKCS7Padding填充,并在调用CCCryptorCreateWithMode()函数时将padding参数赋值为ccNoPadding(不填充)。在作者的基础上我增添了ANSIX923,ISO10126以及Zero填充方式,以及解密之后的去除数据的填充。

    在对称加密中,可以概分为两种模式加密,流加密以及块加密,当我们使用块加密(也就是分组加密)的时候,例如AES、DES,每次是对固定大小的分组数据进行处理。但是大多数需要加密的数据并不是固定大小的倍数长度。例如AES数据块为128位,也就是16字节长度,而需要加密的长度可能为15、26等等。为了解决这个问题,我们就需要对数据进行填补操作,将数据补齐至对应块长度。

补码原理

接下来呢讲一下关于数据填充ANSIX923ISO10126PKCS7以及Zero具体的补码原理。

注,补码原理来自于文章Padding (cryptography),读中文的小伙伴们可以查看这篇 关于PKCS5Padding与PKCS7Padding的区别.
  • ANSIX923 填充方式

    ANSIX923 在填充时首先获取需要填充的字节长度 = (块长度 - (数据长度 % 块长度)), 在填充字节序列中最后一个字节填充为需要填充的字节长度值, 填充字节中其余字节均填充数字零.
    例:

    假定块长度为8 ,数据长度为 10,则填充字节数等于 6,数据等于 FF FF FF FF FF FF FF FF FF DD:
    数据: 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 在填充时首先获取需要填充的字节长度 = (块长度 - (数据长度 % 块长度)), 在填充字节序列中最后一个字节填充为需要填充的字节长度值, 填充字节中其余字节均填充随机数值.
    例:

    假定块长度为 16,数据长度为 9,则填充字节数等于 7,数据等于 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 在填充时首先获取需要填充的字节长度 = (块长度 - (数据长度 % 块长度)), 在填充字节序列中所有字节填充为需要填充的字节长度值
    例:

    假定块长度为 8,数据长度为 3,则填充字节数等于 5,数据等于 FF FF FF :
    | FF FF FF 05 05 05 05 05 |
    
  • Zero 填充方式

    Zero 在填充时首先获取需要填充的字节长度 = (块长度 - (数据长度 % 块长度)), 在填充字节序列中所有字节填充为0x00,零填充在数据最后字节为零的时候可能不可逆
    例:

    假定块长度为 8,数据长度为 2,则填充字节数等于 6,数据等于 FF FF  :
    | FF FF 00 00 00 00 00 00 |
    

特定的,为了使算法可以逆向去除多余的填充字符,所以当数据长度恰好等于块长度的时候,需要补足块长度的字节.例如块长度为8,数据长度为8,则填充字节数等于8.

代码部分

为了可以完成自己的填充算法方便调用,并同时省去PKCS7部分的填充操作,先实现一个枚举并保持系统枚举CCPadding的枚举值也在其中,代码实例如下

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;

并在调用CCCryptorCreateWithMode函数之前,判断填充模式CCPadding paddingMode = ((padding == ccPKCS7Padding) ? ccPKCS7Padding:ccNoPadding) ;,使得我们可以正常使用系统的填充模式之外使用其他自定义填充模式。

下面是iOS部分如何实现对数据进行填充,此函数需要在CCCryptorCreateWithMode函数之前对数据进行处理.

// Fill in the bytes that need to be encrypted.
static NSData * bitPadding(CCOperation operation, CCAlgorithm algorithm ,CcCryptorPadding padding, NSData *data)
{
    //当模式为PKCS7填充时,我们选择使用系统PKCS7填充,返回原始数据,避免二次填充
    if (padding == CcCryptorPKCS7Padding) {
        return  data;
    }
    // 当前处于加密模式,并且不是流加密的情况下.
    if (operation == kCCEncrypt && (algorithm != CcCryptoAlgorithmRC4)  ) {
        //获取一份可操作的数据对象
        NSMutableData *sourceData = data.mutableCopy;
        //设置默认块大小为8.
        int blockSize = 8;
        switch (algorithm) {
            // 当AES加密时,块大小为16.
            case kCCAlgorithmAES:
                blockSize = kCCBlockSizeAES128;
                break;
            case kCCAlgorithmDES:
            case kCCAlgorithm3DES:
            case kCCAlgorithmCAST:
            case kCCAlgorithmBlowfish:
            case kCCAlgorithmRC2:
            default:
                blockSize = 8;
                break;
        }
        // 根据选定填充模式实现不同填充
        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;
}

当我们对数据解密过后,如果不去除填充字节,会发现在解密后的字符串末尾发现填充字节造成的字符
因此,我们需要在解密过后对数据进行去除可能存在的填充字节的处理。代码示例如下:

static NSData * removeBitPadding(CCOperation operation, CCAlgorithm algorithm ,CcCryptorPadding padding, NSData *sourceData)
{
    //若为PKCS7,则交由系统函数处理.
    if (padding == CcCryptorPKCS7Padding) {
        return sourceData;
    }
    //若为解密,并且为块加密
    if (operation == kCCDecrypt && (algorithm != CcCryptoAlgorithmRC4) ) {
        // 设置需要非填充字节长度为0
        int correctLength = 0;
         // 设置块长度默认值为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];
        // 获取最后一位字节.
        char end = testByte[sourceData.length - 1];
        // 根据选定填充模式不同去除可能存在的填充字符
        if (padding == CcCryptorZeroPadding && end == 0) {
            for (int i = (short)sourceData.length - 1; i > 0 ; i--) {
                //若不相等为零字节,则停止
                if (testByte[i] != end) {
                    // 非填充字节的长度为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 则根据最后一位字节的大小判断填充字节的长度以及非填充字节的长度
                correctLength = (short)sourceData.length - end;
            }
        }
        // 直接根据获取的非填充字节的长度以及Byte 生成新的仅有正确字节的data数据.
        NSData *data = [NSData dataWithBytes:testByte length:correctLength];
        return data;
        
    }
    return sourceData;
}

想看源码的可以去Github.

有小伙伴们对CommonCrypto、对称加密感兴趣的可以看看这篇文章iOS 关于CommonCrypto加密 ②(CommonCryptor AES|DES|3DES|CAST|BlowFish)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259

推荐阅读更多精彩内容