1. 程式人生 > IOS開發 >iOS逆向攻防之HASH,數字簽名,對稱加密演算法

iOS逆向攻防之HASH,數字簽名,對稱加密演算法

在密碼學中,加密部分主要分為對稱加密非對稱加密非對稱加密主要有RSA非對稱加密(使用公鑰/私鑰來加密解密),對稱加密主要有DES/3DES/AES對稱加密演算法,順帶提一下我們今天介紹的Hash演算法,Hash屬於一種訊息摘要演算法,不屬於加密演算法,但是由於其單向運算,不可逆性,所以Hash是加密演算法中的構成部分,Hash演算法主要有MD5/Sha1/Sha2,這幾個只是Hash演算法加密精度有所不同。

那麼緊接之前的非對稱加密RSA,直接上這次的乾貨部分

1、Hash概述
2、數字簽名
3、對稱加密演算法簡介
4、對稱加密演算法終端命令
    5、對稱加密演算法終端演練
6、對稱加密演算法程式碼演練
7、CCCrypt函式
複製程式碼

一、Hash概述


1、Hash的概念

Hash,一般翻譯為'雜湊',也有直接音譯的'雜湊',就是把任意長度的輸入,通過雜湊演算法變換成固定長度的輸出,該輸出就是雜湊值。這種轉換是一種壓縮對映,也就是雜湊值的空間通常小於輸入的空間,不同的輸入可能會雜湊成相同的輸出,所以不可能從雜湊值來確定唯一的輸入值。簡單說就是一種將任意長度的訊息壓縮到某一固定長度的訊息摘要的函式。

2、Hash特點

1、演算法是公開的
2、對相同的資料運算,得到的結果是一樣的
3、對於不同的資料運算,如MD5(Hash演算法的一種)的到的結果預設是128位的,32個字元(16進位制標識)
4、沒法逆運算(因為雜湊值是對映關係,會存在雜湊碰撞【即:無限個數據加密得到有限個數據,就存在一個或許多個數據存在同樣的雜湊值】)
5、資訊摘要,資訊’指紋‘,一般是用來做資料識別的(由於沒法做逆運算,所以一般不會用來做加密資料,只是把資料的雜湊值取到,然後用來對比,做資料識別的)

複製程式碼

3、Hash函式(單向雜湊函式)

1、MD5(Message Digest Algorithm 5)
2、SHA(Secure Hash Algorithm)
   SHA又分為:
   SHA-1
   SHA-2系列(224,256,384,512,512/224,512/256統稱為SHA-2系列)
3、MAC(Message Authentication Code)
4、CRC(Cyclic Redundancy Check)
5、SM3(國產雜湊演算法)

複製程式碼

4、Hash用途

1、使用者密碼的加密
2、搜尋引擎
3、版權
4、數字簽名(應用簽名)

複製程式碼

5、HMAC

什麼是HMAC?HMAC(Hash-based message authentication code)是一種使用Hash函式(單向雜湊函式)來構造訊息認證碼的方法,利用雜湊演算法,以一個金鑰和一個訊息為輸入,生成一個訊息摘要作為輸出。主要是為了能讓人對對方身份正確性和訊息有效性進行驗證,與訊息摘要的最大不同,就是有簽名金鑰!

HMAC通過兩次hash兩個不同的key來生成。 目前還沒有發現有任何的方法來產生碰撞。

HMAC中所使用的單向雜湊函式並不僅限於一種,任何高強度的Hash函式(單向雜湊函式)都可以被用於HMAC。 比如使用SHA-1、SHA-224、SHA-256、SHA-384、SHA-512所構造的HMAC,分別稱為HMAC-SHA1、HMAC-SHA-224、HMAC-SHA-384、HMAC-SHA-512。

二、數字簽名


1、什麼是數字簽名

數字簽名就是用於鑑別數字資訊的方法;

2、數字簽名

下面我們以電商支付金額這個場景來描述數字簽名的具體意義:

數字簽名理解示例.png

圖示中,經過RSA加密的原商品資訊Hash值這個整體就叫做數字簽名。

數字簽名的概念.png

三、對稱加密概述


1、對稱加密演算法定義:

對稱加密方式:就是明文通過金鑰加密得到密文。密文通過金鑰解密得到明文。

2、對稱加密常見演算法

1、DES(Data Encryption Standard):資料加密標準,速度較快,適用於加密大量資料的場合;(題外話:實際上用的不多,因為加密強度不夠)
2、3DES(Triple DES):是基於DES的對稱演算法,對相同的資料用3個不同的金鑰執行3次加密,強度更高;(題外話:不過因為3個金鑰管理起來麻煩,所以一般不是很常用~一出生就掛掉了,很慘。。。)
3、RC2和RC4:用變長金鑰對大量資料進行加密,比DES快哦~
4、AES(Advanced Encryption Standard):高階加密標準,是下一代的加密演算法標準,速度快,安全級別高,在21世紀AES標準的一個實現是Rijndael演算法;(題外話:很安全,蘋果的鑰匙串訪問就是用的AES,美國國家安全域性也是用的AES,想要暴力破解基本不可能)

複製程式碼

3、對稱加密應用模式 對稱加密主要有兩種應用模式,下面來詳細介紹一下

ECB(Electronic Code Book):電子密碼本模式。每一塊資料, 獨立加密。

ECB是最基本的加密方式,也就是通常理解的加密,相同的明文將永遠加密成相同的密文,無初始向量,容易受到密碼本重放攻擊,一般情況下很少用。

CBC(Cipher Block Chaining):密碼分組連結模式。使用一個金鑰和一個初始化向量(IV)對資料進行加密。

CBC加密方式,明文被加密前要與前面的密文進行異或運算後再加密,因此只要選擇不同的初始向量,相同的密文加密後會形成不同的密文,這是目前應用最廣泛的模式。CBC加密後的密文是上下文相關的,但明文的錯誤不會傳遞到後續分組,但如果一個分組丟失,後面的分組將全部作廢(同步錯誤).

CBC可以有效的保證密文的完整性,如果一個數據塊在傳遞時丟失或者改變,後面的資料無法進行正常的解密。

四、對稱加密演算法終端命令


AES對稱加密演算法兩種應用模式下的終端命令分別如下: 1、AES(ECB)的加密與解密

AES(ECB)加密'battleMage'字串

$ echo -n battleMage | openssl enc -aes-128-ecb -K 616263 -nosalt | base64

複製程式碼

AES(ECB)解密'battleMage'字串

$ echo -n kXcE5nnetsinAMBEcK6D5g== | base64 -D | openssl enc -aes-128-ecb -K 616263 -nosalt -d

複製程式碼

2、AES(CBC)的加密與解密

AES(CBC)加密'battleMage'字串

$ echo -n battleMage | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt | base64

複製程式碼

AES(CBC)解密'battleMage'字串

$ echo -n H3tn3dXCEtKNvijJYLsStw== | base64 -D | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -d

複製程式碼

五、對稱加密演算法終端演練對比


1、新建一個message.txt文字檔案

$ vi message.txt

複製程式碼

回車進入編輯介面,點選'i',進入編輯介面,輸入5排'1234567890',點選'esc',再點選'shift+:',輸入'wq'回車儲存。

message.txt檔案.png

2、對該'message.txt'檔案直接使用AES(ECB)進行加密,然後輸出一個'meg1.bin'檔案

$ openssl enc -des-ecb -K 616263 -nosalt -in message.txt -out meg1.bin

複製程式碼

直接敲回車,得到一個 meg1.bin 的檔案

然後直接修改message.txt檔案,把最後一排的第一個1改成2,

AES(ECB)修改之後再次加密.png

再次使用上述命令進行加密,然後輸出一個'meg2.bin'檔案

$ openssl enc -des-ecb -K 616263 -nosalt -in message.txt -out meg2.bin

複製程式碼

直接敲回車,得到一個 meg2.bin 的檔案

接下來使用xxd命令檢視meg1.bin 和 meg2.bin檔案

同樣通過AES(CBC)加密'message.txt'並輸出一個‘meg3.bin’檔案

$ openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -in message.txt -out meg3.bin

複製程式碼

再次手動編輯message.txt檔案,把message.txt還原,然後通過AES(CBC)加密並輸出一個‘meg4.bin’檔案

$ openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -in message.txt -out meg4.bin
複製程式碼

對比如下圖,

AES兩種方式ECB和CBC的區別.png

六、對稱加密演算法程式碼演練


接下來開始程式碼演練部分,需要匯入一個工具類,工具類程式碼並不多,這裡直接貼工具類的內容吧,工具類標頭檔案AES,DES各種終端命令也都包含在內了:

.h檔案

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>

/**
 *  終端測試指令
 *
 *  DES(ECB)加密
 *  $ echo -n hello | openssl enc -des-ecb -K 616263 -nosalt | base64
 *
 * DES(CBC)加密
 *  $ echo -n hello | openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt | base64
 *
 *  AES(ECB)加密
 *  $ echo -n hello | openssl enc -aes-128-ecb -K 616263 -nosalt | base64
 *
 *  AES(CBC)加密
 *  $ echo -n hello | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt | base64
 *
 *  DES(ECB)解密
 *  $ echo -n HQr0Oij2kbo= | base64 -D | openssl enc -des-ecb -K 616263 -nosalt -d
 *
 *  DES(CBC)解密
 *  $ echo -n alvrvb3Gz88= | base64 -D | openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt -d
 *
 *  AES(ECB)解密
 *  $ echo -n d1QG4T2tivoi0Kiu3NEmZQ== | base64 -D | openssl enc -aes-128-ecb -K 616263 -nosalt -d
 *
 *  AES(CBC)解密
 *  $ echo -n u3W/N816uzFpcg6pZ+kbdg== | base64 -D | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -d
 *
 *  提示:
 *      1> 加密過程是先加密,再base64編碼
 *      2> 解密過程是先base64解碼,再解密
 */
@interface EncryptionTools : NSObject
    
+ (instancetype)sharedEncryptionTools;
    
    /**
     @constant   kCCAlgorithmAES     高階加密標準,128位(預設)
     @constant   kCCAlgorithmDES     資料加密標準
     */
    @property (nonatomic,assign) uint32_t algorithm;
    
    /**
     *  加密字串並返回base64編碼字串
     *
     *  @param string    要加密的字串
     *  @param keyString 加密金鑰
     *  @param iv        初始化向量(8個位元組)
     *
     *  @return 返回加密後的base64編碼字串
     */
- (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv;
    
    /**
     *  解密字串
     *
     *  @param string    加密並base64編碼後的字串
     *  @param keyString 解密金鑰
     *  @param iv        初始化向量(8個位元組)
     *
     *  @return 返回解密後的字串
     */
- (NSString *)decryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv;
    
    @end


複製程式碼

.m檔案

#import "EncryptionTools.h"

@interface EncryptionTools()
    @property (nonatomic,assign) int keySize;
    @property (nonatomic,assign) int blockSize;
    @end

@implementation EncryptionTools
    
+ (instancetype)sharedEncryptionTools {
    static EncryptionTools *instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
        instance = [[self alloc] init];
        instance.algorithm = kCCAlgorithmAES;
    });
    
    return instance;
}
    
- (void)setAlgorithm:(uint32_t)algorithm {
    _algorithm = algorithm;
    switch (algorithm) {
        case kCCAlgorithmAES:
        self.keySize = kCCKeySizeAES128;
        self.blockSize = kCCBlockSizeAES128;
        break;
        case kCCAlgorithmDES:
        self.keySize = kCCKeySizeDES;
        self.blockSize = kCCBlockSizeDES;
        break;
        default:
        break;
    }
}
    
- (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
    
    // 設定祕鑰
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t cKey[self.keySize];
    bzero(cKey,sizeof(cKey));
    [keyData getBytes:cKey length:self.keySize];
    
    // 設定iv
    uint8_t cIv[self.blockSize];
    bzero(cIv,self.blockSize);
    int option = 0;
    if (iv) {
        [iv getBytes:cIv length:self.blockSize];
        option = kCCOptionPKCS7Padding;
    } else {
        option = kCCOptionPKCS7Padding | kCCOptionECBMode;
    }
    
    // 設定輸出緩衝區
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    size_t bufferSize = [data length] + self.blockSize;
    void *buffer = malloc(bufferSize);
    
    // 開始加密
    size_t encryptedSize = 0;
    //加密解密都是它 -- CCCrypt
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,self.algorithm,option,cKey,self.keySize,cIv,[data bytes],[data length],buffer,bufferSize,&encryptedSize);
    
    NSData *result = nil;
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize];
    } else {
        free(buffer);
        NSLog(@"[錯誤] 加密失敗|狀態編碼: %d",cryptStatus);
    }
    
    return [result base64EncodedStringWithOptions:0];
}
    
- (NSString *)decryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
    
    // 設定祕鑰
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t cKey[self.keySize];
    bzero(cKey,self.blockSize);
    int option = 0;
    if (iv) {
        [iv getBytes:cIv length:self.blockSize];
        option = kCCOptionPKCS7Padding;
    } else {
        option = kCCOptionPKCS7Padding | kCCOptionECBMode;
    }
    
    // 設定輸出緩衝區
    NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
    size_t bufferSize = [data length] + self.blockSize;
    void *buffer = malloc(bufferSize);
    
    // 開始解密
    size_t decryptedSize = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,&decryptedSize);
    
    NSData *result = nil;
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
    } else {
        free(buffer);
        NSLog(@"[錯誤] 解密失敗|狀態編碼: %d",cryptStatus);
    }
    
    return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}
    
    @end

複製程式碼

接下來新建工程,把工具類.h,.m拖入工程,在ViewController.m實現touchBegin方法

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSString * key = @"abc";
    uint8_t iv[8] = {1,2,3,4,5,6,7,8};
    
    //備註:選擇的是AES(ECB),初始向量iv直接傳nil,如果選擇 AES(CBC),初始向量iv需要傳值,具體可見工具類.m檔案中實現,是判斷iv是否為nil來選取加密方式的
    //1、選擇AES(ECB)
    NSString * encStr =  [[EncryptionTools sharedEncryptionTools]encryptString:@"hello" keyString:key iv:nil];
    
    NSLog(@"AES(ECB)加密的結果是:%@",encStr);
    NSLog(@"AES(ECB)解密的結果是:%@",[[EncryptionTools sharedEncryptionTools]decryptString:encStr keyString:key iv:nil]);
    
    //2、選擇AES(CBC)
    NSData * ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
    NSString * encStr1 =  [[EncryptionTools sharedEncryptionTools]encryptString:@"hello" keyString:key iv:ivData];
    
    NSLog(@"AES(CBC)加密的結果是:%@",encStr1);
    NSLog(@"AES(CBC)解密的結果是:%@",[[EncryptionTools sharedEncryptionTools]decryptString:encStr1 keyString:key iv:ivData]);
    
}


@end


複製程式碼

點選執行,執行結果OK

2019-10-12 21:57:26.858675+0800 CryptDemo[1790:115503] AES(ECB)加密的結果是:d1QG4T2tivoi0Kiu3NEmZQ==
2019-10-12 21:57:26.858896+0800 CryptDemo[1790:115503] AES(ECB)解密的結果是:hello
2019-10-12 21:57:26.859040+0800 CryptDemo[1790:115503] AES(CBC)加密的結果是:u3W/N816uzFpcg6pZ+kbdg==
2019-10-12 21:57:26.859194+0800 CryptDemo[1790:115503] AES(CBC)解密的結果是:hello

複製程式碼

七、CCCrypt函式


第六步已經使用過封裝好的加密工具類EncryptionTools.h,這個工具類只是封裝了CCCrypt函式,下面我們來研究一下加密工具的核心函式CCCrypt函式:

使用CCCrypt函式,需要引入系統庫

#import <CommonCrypto/CommonCrypto.h>

複製程式碼

不管是加密還是解密都是使用這個函式,下面我們來介紹一下這個函式中的引數,引數解釋我直接備註在API的後面,注意裡面有坑!!!

CCCrypt函式

引數介紹
1、 CCOperation op :操作型別:加密or解密,列舉值;
    kCCEncrypt 代表加密
    kCCDecrypt 代表解密

2、 CCAlgorithm alg:加密演算法,列舉值;
    kCCAlgorithmAES     高階加密標準,128位(預設)
    kCCAlgorithmDES     資料加密標準
    
3、 CCOptions options:加密應用模式,列舉值;
	注意注意!!!!!!!!!!這裡有個坑;kCCOptionPKCS7Padding代表填充模式,這個options必須加上填充模式;
    CCCrypt的option預設是CBC,所以只需要補充一個填充模式就能代表CBC; 但是ECB就需要額外再加上一個kCCOptionECBMode,所以選擇ECB就需要kCCOptionPKCS7Padding | kCCOptionECBMode;
    
    所以想要選擇CBC和ECB,需要按下面進行填寫!
	kCCOptionPKCS7Padding;  代表CBC
	kCCOptionPKCS7Padding | kCCOptionECBMode; 代表ECB

4、 const void *key :加密的金鑰的指標
5、 size_t keyLength:金鑰的長度
6、 const void *iv: 初始化向量
7、 const void *dataIn:加密的原始資料
8、 size_t dataInLength:加密的原始資料的長度
9、 void *dataOut:加密後密文的記憶體地址
10、size_t dataOutAvailable:加密後密文的緩衝區大小
11、size_t *dataOutMoved :加密結果的大小


CCCryptorStatus CCCrypt(
    CCOperation op,/* kCCEncrypt,etc. */
    CCAlgorithm alg,/* kCCAlgorithmAES128,etc. */
    CCOptions options,/* kCCOptionPKCS7Padding,etc. */
    const void *key,size_t keyLength,const void *iv,/* optional initialization vector */
    const void *dataIn,/* optional per op and alg */
    size_t dataInLength,void *dataOut,/* data RETURNED here */
    size_t dataOutAvailable,size_t *dataOutMoved)


複製程式碼

需要注意的是,直接使用這個函式安全隱患非常的大!因為這個函式是系統提供的,不管你是加密還是解密,都是呼叫了CCCrypt函式,而黑客可以通過越獄手機附加除錯或者是非越獄手機重籤除錯,能夠用函式斷點斷到你的CCCrypt函式,然後通過暫存器直接獲取函式的對應引數,根據上面函式的介紹,我們的資料其實是其中的第七個引數'const void *dataIn',第七個引數的下標為6,然後調用匯編指令'x6',就可以拿到你的資料的地址,然後轉一下型別,就能直接打印出你的加密資料!具體操作如下:

1、還是開啟之前的工程,設定函式斷點CCCrypt,然後使用真機執行!!!必須用真機,因為真機和模擬器的CPU不一樣

1、符號斷點設定為CCCrypt.png

2、執行工程,模擬黑客除錯,然後點選螢幕出發touchBegin方法,然後斷點停在了CCCrypt函式的地方

3、因為函式在呼叫的時候,都是存在CPU的暫存器上,輸入暫存器檢視指令

register read x6

複製程式碼

read x6是讀取該函式對應的第7個引數,第一個引數是x0

4、拿到地址,然後強轉型別,蒙圈了吧,你的資料就洩漏了

2、暫存器讀取加密的資料.png

所以這個函式不能直接使用,現在只說基礎,後面會詳細說安全防護~今天就說到這裡了~