密碼學基礎(二):對稱加密

什麼是對稱加密

加密和解密使用相同的祕鑰稱爲對稱加密。算法

主流的對稱加密算法

DES:已經淘汰 3DES:相對於DES有所增強,可是仍然存在較大風險 AES:全新的對稱加密算法。數組

對稱加密的特色

特色決定使用場景,對稱加密擁有以下特色:安全

快速

速度快,可用於頻率很高的加密場景。bash

可逆

使用同一個祕鑰進行加密和解密。函數

分組加密

可選按照12八、19二、256位爲一組的加密方式,加密後的輸出值爲所選分組位數的倍數。密鑰的長度不一樣,推薦加密輪數也不一樣,加密強度也更強。ui

例如: AES加密結果的長度由原字符串長度決定:一個字符爲1byte=4bit,一個字符串爲n+1byte,由於最後一位爲'\0',因此當字符串長度小於等於15時,AES128獲得的16進制結果爲32位,也就是324=128byte,當長度超過15時,就是64位爲1282byte。編碼

對稱加密的使用場景

由於對稱加密速度快的特色,對稱加密被普遍運用在各類加密場所中。可是由於其須要傳遞祕鑰,一旦祕鑰被截獲或者泄露,其加密就會玩徹底破解,因此AES通常和RSA一塊兒使用。加密

由於RSA不用傳遞祕鑰,加密速度慢,因此通常使用RSA加密AES中鎖使用的祕鑰後,再傳遞祕鑰,保證祕鑰的安全。祕鑰安全傳遞成功後,一直使用AES對會話中的信息進行加密,以此來解決AES和RSA的缺點並完美髮揮二者的優勢,其中相對經典的例子就是HTTPS加密,後文會專門研究。spa

AES加密算法的基本原理

本文針對ECB模式下的AES算法進行大概講解,針對每一步的詳細算法再也不該文討論範圍內。3d

一、明文分組

128位的明文被分紅16個字節的明文矩陣,而後將明文矩陣轉化成狀態矩陣,以「abcdefghijklmnop」的明文爲例:

明文矩陣

二、密文分組

一樣的,128位密鑰被分紅16組的狀態矩陣。與明文不一樣的是,密文會以列爲單位,生成最初的4x8x4=128的祕鑰,也就是一個組中有4個元素,每一個元素由每列中的4個祕鑰疊加而成,其中矩陣中的每一個祕鑰爲1個字節也就是8位。

生成初始的w[0]、w[1]、w[2]、w[3]原始密鑰以後,經過密鑰編排函數,該密鑰矩陣被擴展成一個44個組成的序列W[0],W[1], … ,W[43]。該序列的前4個元素W[0],W[1],W[2],W[3]是原始密鑰,用於加密運算中的初始密鑰加,後面40個字分爲10組,每組4個32位的字段組成,總共爲128位,分別用於10輪加密運算中的輪密鑰加密,以下圖所示:

祕鑰編排函數

三、輪密鑰加

之因此把這一步單獨提出來,是由於ECB和CBC模式中主要的區別就在這一步。

ECB模式中,初始祕鑰擴展後生成祕鑰組後(w0-w43),明文根據當前輪數取出w[i,i+3]進行加密操做。

CBC模式中,則使用前一輪的密文(明文加密以後的值)和當前的明文進行異或操做以後再進行加密操做。如圖所示:

ECB和CBC

四、剩餘輪操做

根據不一樣位數分組,官方推薦的加密輪數:

AES加密輪數
輪操做加密的第1輪到第9輪的輪函數同樣,包括4個操做:字節代換、行位移、列混合和輪密鑰加。最後一輪迭代不執行列混合。
輪加密

五、按照組數對每組進行輪操做

當第一組加密完成時,後面的組循環進行加密操做知道全部的組都完成加密操做。

六、轉碼

通常會將結果轉化成base64位,此時在iOS中應該使用base64編碼的方式進行解碼操做,而不是UTF-8。

Base64基本介紹

base64是一種編碼方式,經常使用語傳輸8bit字節碼。其編碼原理以下所示:

一、取原數據

將原數據按照3個字節取爲一組,即爲3x8=24位

二、分組

將3x8=24的數據分爲4x6=24的數據,也就是分爲了4組

三、高位補0

將4個組中的數據分別在高位補上2個0,也就成了8x4=32,因此原數據增大了三分之一。

四、轉碼

根據base64編碼表對數據進行轉換,若是要編碼的二進制數據不是3的倍數,最後會剩下1個或2個字節怎麼辦,Base64用\x00字節在末尾補足後,再在編碼的末尾加上1個或2個=號,表示補了多少字節,解碼的時候,會自動去掉。

舉個栗子:Man最後的結果就是TWFu。

base64編碼原理

爲何須要高位補0
由於base64產生的基礎是避免在傳輸8Bit字節碼數據時,不可見字符的可能產生的一些錯誤。由於字符是以8位爲一個字節來處理,補零隻是爲了方便當前的處理規則,若是是6位,那麼又須要作適配或者更改當前的處理規則。

base64編碼的意義

計算機中全部的數據都是以0和1的二進制來存儲,而全部的文字都是經過ascii錶轉化而來進而顯示成對應的語言。可是ascii表中存在許多不可見字符,這些不可見字符在數據傳輸時,有可能通過不一樣硬件上各類類型的路由,在轉義時容易發生錯誤,因此規定了64個可見字符(a-z、A-Z、0-九、+、/),經過base64轉碼以後,全部的二進制數據都是可見的。

ECB && CBC

ECB和CBC是兩種加密工做模式。其相同點都是在開始輪加密以前,將明文和密文按照128/192/256進行分組。以128位爲例,明文和密文都分爲16組,每組1個字節爲8位。

ECB工做模式中,每一組的明文和密文相互獨立,每一組的明文經過對應該組的密文加密後生成密文,不影響其餘組。

CBC工做模式中,後一組的明文在加密以前先使用前一組的密文進行異或運算後再和對應該組的密文進行加密操做生成密文。

爲簡單的分組加密。將明文和密文分紅若干組後,使用密文對明文進行加密生成密文 CBC

對稱加密在iOS中的使用

加密:

- (NSString *)AES128EncryptWithKey:(NSString *)key plainText:(NSString *)plainText {
    char keyPtr[kCCKeySizeAES128];
    // 初始化數組
    memset(keyPtr, 0, sizeof(keyPtr));
    
    // NSString->char[]
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    // 原文處理
    NSData *data = [plainText dataUsingEncoding:NSUTF8StringEncoding];
    NSUInteger dataLength = [data length];
    
    // 密文輸出佔位
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesEncrypted = 0;
    
    // 加密
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding | kCCOptionECBMode, keyPtr, kCCBlockSizeAES128, NULL, [data bytes], dataLength, buffer, bufferSize, &numBytesEncrypted);
    
    if (cryptStatus == kCCSuccess) {
        NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
        NSString *stringBase64 = [resultData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
        return stringBase64;
    }
    free(buffer);
    return nil;
}
複製代碼

解密:

- (NSString *)AES128DecryptWithKey:(NSString *)key secret:(NSString *)secret {
    char keyPtr[kCCKeySizeAES128];
    memset(keyPtr, 0, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    //base64解碼
    NSData *data = [[NSData alloc] initWithBase64EncodedString:secret options:NSDataBase64DecodingIgnoreUnknownCharacters];
    
    NSUInteger dataLength = [data length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesCrypted = 0;
    
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding | kCCOptionECBMode, keyPtr, kCCBlockSizeAES128, NULL, [data bytes], dataLength, buffer, bufferSize, &numBytesCrypted);
    
    if (cryptStatus == kCCSuccess) {
        NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
        
        return [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
    }
    free(buffer);
    return nil;
}
複製代碼

歡迎關注
相關文章
相關標籤/搜索