iOS AES加密

AES:(Advanced Encryption Standard)高級加密標(biāo)準(zhǔn)软啼。是一個(gè)對稱分組秘鑰算法祸挪,旨在取代 DES 成為廣泛使用的標(biāo)準(zhǔn)贞间。

秘鑰長度有三種增热,分別是 AES-128、AES-192 和 AES-256公黑。

加密模式有四種摄咆,分別是 ECB(Elecyronic Code Book,電子密碼本)、CBC(Cipher Block Chaining,加密塊鏈)吭从、CFB(Cipher FeedBack Mode,加密反饋)朝蜘、OFB(Output FeedBack,輸出反饋)。

填充模式:
由于塊加密只能對特定長度的數(shù)據(jù)塊進(jìn)行加密涩金,因此CBC谱醇、ECB模式需要在最后一數(shù)據(jù)塊加密前進(jìn)行數(shù)據(jù)填充。(CFB步做,OFB和CTR模式由于與key進(jìn)行加密操作的是上一塊加密后的密文副渴,因此不需要對最后一段明文進(jìn)行填充)

在iOS SDK中提供了PKCS7Padding,而JDK則提供了PKCS5Padding全度。原則上PKCS5Padding限制了填充的Block Size為8 bytes煮剧,而Java實(shí)際上當(dāng)塊大于該值時(shí)讼载,其PKCS5Padding與PKCS7Padding是相等的轿秧。

初始向量(偏移量)
使用除ECB以外的其他加密模式均需要傳入一個(gè)初始向量,其大小與Block Size相等(AES的Block Size為128 bits(16字節(jié)))咨堤,而兩個(gè)平臺的API文檔均指明當(dāng)不傳入初始向量時(shí)菇篡,系統(tǒng)將默認(rèn)使用一個(gè)全0的初始向量。

引入頭文件

import <CommonCrypto/CommonDigest.h>

import <CommonCrypto/CommonCryptor.h>

+ (NSString *)SB_AES_Encrypt:(NSString *)originalStr
                      mode:(SBMode)mode
                       key:(NSString *)key
                   keySize:(SBKeySizeAES)keySize
                        iv:(NSString * _Nullable )iv
                   padding:(SBCryptorPadding)padding;
{
    NSData *data = [originalStr dataUsingEncoding:NSUTF8StringEncoding];
    data = [self SB_AES_WithData:data operation:kCCEncrypt mode:mode key:key keySize:keySize iv:iv padding:padding];
    return [data base64EncodedStringWithOptions:0];
}

+ (NSString *)SB_AES_Decrypt:(NSString *)originalStr
                      mode:(SBMode)mode
                       key:(NSString *)key
                   keySize:(SBKeySizeAES)keySize
                        iv:(NSString * _Nullable )iv
                   padding:(SBCryptorPadding)padding
{
    NSData *data = [[originalStr dataUsingEncoding:NSUTF8StringEncoding] base64EncodedDataWithOptions:0];
    
    data = [self SB_AES_WithData:data operation:kCCDecrypt mode:mode key:key keySize:keySize iv:iv padding:padding];
    return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}

+ (NSData *)SB_AES_WithData:(NSData *)originalData
                 operation:(CCOperation)operation
                      mode:(CCMode)mode
                       key:(NSString *)key
                   keySize:(SBKeySizeAES)keySize
                        iv:(NSString *)iv
                   padding:(SBCryptorPadding)padding
{
    NSAssert((mode != kCCModeECB && iv != nil && iv != NULL) || mode == kCCModeECB, @"使用 CBC 模式一喘,initializationVector(即iv驱还,填充值)必須有值");
    
    CCCryptorRef cryptor = NULL;
    CCCryptorStatus status = kCCSuccess;
    
    NSMutableData * keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
    NSMutableData * ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
    
#if !__has_feature(objc_arc)
    [keyData autorelease];
    [ivData autorelease];
#endif
    
    [keyData setLength:keySize];
    [ivData setLength:keySize];
    
    //填充模式(系統(tǒng)API只提供了兩種)
    CCPadding paddingMode = (padding == ccPKCS7Padding) ? ccPKCS7Padding : ccNoPadding ;
    NSData *sourceData = originalData;
    if (operation == kCCEncrypt) {
        sourceData =  [self bitPaddingWithData:originalData mode:mode padding:padding];    //FIXME: 實(shí)際上的填充模式
    }
    
    status = CCCryptorCreateWithMode(operation, mode, kCCAlgorithmAES, paddingMode, ivData.bytes, keyData.bytes, keyData.length, NULL, 0, 0, 0, &cryptor);
    if ( status != kCCSuccess ){
        NSLog(@"Encrypt Error:%d",status);
        return nil;
    }
    
    //確定處理給定輸入所需的輸出緩沖區(qū)大小尺寸。
    size_t bufsize = CCCryptorGetOutputLength( cryptor, (size_t)[sourceData length], true );
    void * buf = malloc( bufsize );
    size_t bufused = 0;
    size_t bytesTotal = 0;
    
    //處理(加密凸克,解密)一些數(shù)據(jù)议蟆。如果有結(jié)果的話,寫入提供的緩沖區(qū).
    status = CCCryptorUpdate( cryptor, [sourceData bytes], (size_t)[sourceData length],
                             buf, bufsize, &bufused );
    if ( status != kCCSuccess ){
        NSLog(@"Encrypt Error:%d",status);
        free( buf );
        return nil;
    }
    bytesTotal += bufused;
    if (padding == SBCryptorPKCS7Padding) {
        status = CCCryptorFinal( cryptor, buf + bufused, bufsize - bufused, &bufused );
        if ( status != kCCSuccess ){
            NSLog(@"Encrypt Error:%d",status);
            free( buf );
            return nil;
        }
        bytesTotal += bufused;
    }
    
    NSData *result = [NSData dataWithBytesNoCopy:buf length: bytesTotal];
    if (operation == kCCDecrypt) {
        //解密時(shí)移除填充
        result = [self removeBitPaddingWithData:result mode:mode operation:operation andPadding:padding];
    }
    
    CCCryptorRelease(cryptor);

    return result;
}

// 填充需要加密的字節(jié)
+ (NSData *)bitPaddingWithData:(NSData *)data
                          mode:(CCMode)mode
                       padding:(SBCryptorPadding)padding;
{
    NSMutableData *sourceData = data.mutableCopy;
    int blockSize = kCCBlockSizeAES128;         //FIXME: AES的塊大小都是128bit,即16bytes
    
    switch (padding) {
        case SBCryptorPKCS7Padding:
        {
            if (mode == kCCModeCFB || mode == kCCModeOFB) {
                //MARK: CCCryptorCreateWithMode方法在這兩個(gè)模式下萎战,并不會給塊自動(dòng)填充咐容,所以需要手動(dòng)去填充
                NSUInteger shouldLength = blockSize * ((sourceData.length / blockSize) + 1);
                NSUInteger diffLength = shouldLength - sourceData.length;
                uint8_t *bytes = malloc(sizeof(*bytes) * diffLength);
                for (NSUInteger i = 0; i < diffLength; i++) {
                    // 補(bǔ)全缺失的部分
                    bytes[i] = diffLength;
                }
                [sourceData appendBytes:bytes length:diffLength];
            }
        }
            break;
        case SBCryptorZeroPadding:
        {
            int pad = 0x00;
            int diff =   blockSize - (sourceData.length % blockSize);
            for (int i = 0; i < diff; i++) {
                [sourceData appendBytes:&pad length:1];
            }
        }
            break;
        case SBCryptorANSIX923:
        {
            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;
        case SBCryptorISO10126:
        {
            int diff = blockSize - (sourceData.length % blockSize);
            for (int i = 0; i < diff - 1; i++) {
                int pad  = arc4random() % 254 + 1;      //FIXME: 因?yàn)槭请S機(jī)填充,所以相同參數(shù)下蚂维,每次加密都是不一樣的結(jié)果(除了分段后最后一個(gè)分段的長度為15bytes的時(shí)候加密結(jié)果相同)
                [sourceData appendBytes:&pad length:1];
            }
            [sourceData appendBytes:&diff length:1];
        }
            break;
        default:
            break;
    }
    return sourceData;
}

+ (NSData *)removeBitPaddingWithData:(NSData *)sourceData mode:(CCMode)mode operation:(CCOperation)operation andPadding:(SBCryptorPadding)padding
{
    int correctLength = 0;
    int blockSize = kCCBlockSizeAES128;
    Byte *testByte = (Byte *)[sourceData bytes];
    char end = testByte[sourceData.length - 1];
    
    if (padding == SBCryptorPKCS7Padding) {
        if ((mode == kCCModeCFB || mode == kCCModeOFB) && (end > 0 && end < blockSize + 1)) {
            correctLength = (short)sourceData.length - end;
        }else{
            return sourceData;
        }
    }else if (padding == SBCryptorZeroPadding && end == 0) {
        for (int i = (short)sourceData.length - 1; i > 0 ; i--) {
            if (testByte[i] != end) {
                correctLength = i + 1;
                break;
            }
        }
    }else if ((padding == SBCryptorANSIX923 || padding == SBCryptorISO10126) && (end > 0 && end < blockSize + 1)){
        correctLength = (short)sourceData.length - end;
//        if (padding == MIUCryptorISO10126 || ( testByte[sourceData.length - end] == 0 && testByte[sourceData.length - 2] == 0)) {
//            correctLength = (short)sourceData.length - end;
//        }
    }
    
    NSData *data = [NSData dataWithBytes:testByte length:correctLength];
    return data;
}

Demo代碼請參考gitHub:https://github.com/Manchitor/SBTools

import "SBTools+AES.h"

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末戳粒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子虫啥,更是在濱河造成了極大的恐慌蔚约,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涂籽,死亡現(xiàn)場離奇詭異苹祟,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)评雌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門树枫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柳骄,你說我怎么就攤上這事团赏。” “怎么了耐薯?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵舔清,是天一觀的道長。 經(jīng)常有香客問我曲初,道長体谒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任臼婆,我火速辦了婚禮抒痒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘颁褂。我一直安慰自己故响,他們只是感情好傀广,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著彩届,像睡著了一般伪冰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上樟蠕,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天贮聂,我揣著相機(jī)與錄音,去河邊找鬼寨辩。 笑死吓懈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的靡狞。 我是一名探鬼主播耻警,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼耍攘!你這毒婦竟也來了榕栏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蕾各,失蹤者是張志新(化名)和其女友劉穎扒磁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體式曲,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妨托,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吝羞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兰伤。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钧排,靈堂內(nèi)的尸體忽然破棺而出敦腔,到底是詐尸還是另有隱情,我是刑警寧澤恨溜,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布符衔,位于F島的核電站,受9級特大地震影響糟袁,放射性物質(zhì)發(fā)生泄漏判族。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一项戴、第九天 我趴在偏房一處隱蔽的房頂上張望形帮。 院中可真熱鬧,春花似錦、人聲如沸辩撑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽合冀。三九已至锄蹂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間水慨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工敬扛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晰洒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓啥箭,卻偏偏與公主長得像谍珊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子急侥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

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

  • 前言 1. 介紹1.1 AES是什么砌滞?1.2 AES詳解1.3 實(shí)現(xiàn)原理和比較1.4 模式和填充選擇 2. 經(jīng)驗(yàn)...
    LuckyCat_A閱讀 11,110評論 6 21
  • 介紹 AES:(Advanced Encryption Standard)高級加密標(biāo)準(zhǔn)。是一個(gè)對稱分組秘鑰算法坏怪,旨...
    MrSYLong閱讀 3,873評論 1 3
  • AES是開發(fā)中常用的加密算法之一贝润。然而由于前后端開發(fā)使用的語言不統(tǒng)一,導(dǎo)致經(jīng)常出現(xiàn)前端加密而后端不能解密的情況出現(xiàn)...
    那C亂我心閱讀 1,798評論 0 1
  • 一:前言 AES是開發(fā)中常用的加密算法之一。然而由于前后端開發(fā)使用的語言不統(tǒng)一鹏秋,導(dǎo)致經(jīng)常出現(xiàn)前端加密而后端不能解密...
    LikeSomeBody閱讀 2,394評論 1 1
  • AES是開發(fā)中常用的加密算法之一尊蚁。然而由于前后端開發(fā)環(huán)境差異,導(dǎo)致出現(xiàn)前端加密而后端不能解密的情況出現(xiàn)侣夷。然而無論什...
    醉臥欄桿聽雨聲閱讀 2,745評論 3 3