在密碼學中,加密部分主要分為對稱加密和非對稱加密,非對稱加密主要有RSA非對稱加密(使用公鑰/私鑰來加密解密),對稱加密主要有DES/3DES/AES對稱加密算法,順帶提一下我們今天介紹的Hash算法,Hash屬于一種消息摘要算法,不屬于加密算法,但是由于其單向運算,不可逆性,所以Hash是加密算法中的構成部分,Hash算法主要有MD5/Sha1/Sha2,這幾個只是Hash算法加密精度有所不同。
那么緊接之前的非對稱加密RSA,直接上這次的干貨部分
1、Hash概述2、數(shù)字簽名3、對稱加密算法簡介4、對稱加密算法終端命令? ? 5、對稱加密算法終端演練6、對稱加密算法代碼演練7、CCCrypt函數(shù)復制代碼
一、Hash概述
1、Hash的概念
Hash, 一般翻譯為'散列', 也有直接音譯的'哈希',就是把任意長度的輸入,通過散列算法變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是散列值的空間通常小于輸入的空間,不同的輸入可能會散列成相同的輸出,所以不可能從散列值來確定唯一的輸入值。簡單說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數(shù)。
2、Hash特點
1、算法是公開的2、對相同的數(shù)據(jù)運算,得到的結果是一樣的3、對于不同的數(shù)據(jù)運算,如MD5(Hash算法的一種)的到的結果默認是128位的,32個字符(16進制標識)4、沒法逆運算(因為哈希值是映射關系,會存在散列碰撞【即:無限個數(shù)據(jù)加密得到有限個數(shù)據(jù),就存在一個或許多個數(shù)據(jù)存在同樣的哈希值】)5、信息摘要,信息’指紋‘,一般是用來做數(shù)據(jù)識別的(由于沒法做逆運算,所以一般不會用來做加密數(shù)據(jù),只是把數(shù)據(jù)的哈希值取到,然后用來對比,做數(shù)據(jù)識別的)復制代碼
3、Hash函數(shù)(單向散列函數(shù))
1、MD5(Message Digest Algorithm 5)2、SHA(Secure Hash Algorithm)? SHA又分為:? SHA-1? SHA-2系列(224,256,384,512,512/224,512/256統(tǒng)稱為SHA-2系列)3、MAC(Message Authentication Code)4、CRC(Cyclic Redundancy Check)5、SM3(國產(chǎn)哈希算法)復制代碼
4、Hash用途
1、用戶密碼的加密2、搜索引擎3、版權4、數(shù)字簽名(應用簽名)復制代碼
5、HMAC
什么是HMAC?HMAC(Hash-based message authentication code)是一種使用Hash函數(shù)(單向散列函數(shù))來構造消息認證碼的方法,利用哈希算法,以一個密鑰和一個消息為輸入,生成一個消息摘要作為輸出。主要是為了能讓人對對方身份正確性和消息有效性進行驗證,與消息摘要的最大不同,就是有簽名密鑰!
HMAC通過兩次hash兩個不同的key來生成。 目前還沒有發(fā)現(xiàn)有任何的方法來產(chǎn)生碰撞。
HMAC中所使用的單向散列函數(shù)并不僅限于一種,任何高強度的Hash函數(shù)(單向散列函數(shù))都可以被用于HMAC。
比如使用SHA-1、SHA-224、SHA-256、SHA-384、SHA-512所構造的HMAC,分別稱為HMAC-SHA1、HMAC-SHA-224、HMAC-SHA-384、HMAC-SHA-512。
二、數(shù)字簽名
1、什么是數(shù)字簽名
數(shù)字簽名就是用于鑒別數(shù)字信息的方法;
2、數(shù)字簽名
下面我們以電商支付金額這個場景來描述數(shù)字簽名的具體意義:

圖示中,經(jīng)過RSA加密的原商品信息Hash值這個整體就叫做數(shù)字簽名。

三、對稱加密概述
1、對稱加密算法定義:
對稱加密方式:就是明文通過密鑰加密得到密文。密文通過密鑰解密得到明文。
2、對稱加密常見算法
1、DES(Data Encryption Standard):數(shù)據(jù)加密標準,速度較快,適用于加密大量數(shù)據(jù)的場合;(題外話:實際上用的不多,因為加密強度不夠)2、3DES(Triple DES):是基于DES的對稱算法,對相同的數(shù)據(jù)用3個不同的密鑰執(zhí)行3次加密,強度更高;(題外話:不過因為3個密鑰管理起來麻煩,所以一般不是很常用~一出生就掛掉了,很慘。。。)3、RC2和RC4:用變長密鑰對大量數(shù)據(jù)進行加密,比DES快哦~4、AES(Advanced Encryption Standard):高級加密標準,是下一代的加密算法標準,速度快,安全級別高,在21世紀AES標準的一個實現(xiàn)是Rijndael算法;(題外話:很安全,蘋果的鑰匙串訪問就是用的AES,美國國家安全局也是用的AES,想要暴力破解基本不可能)復制代碼
3、對稱加密應用模式對稱加密主要有兩種應用模式,下面來詳細介紹一下
ECB(Electronic Code Book):電子密碼本模式。每一塊數(shù)據(jù), 獨立加密。
ECB是最基本的加密方式,也就是通常理解的加密,相同的明文將永遠加密成相同的密文,無初始向量,容易受到密碼本重放攻擊,一般情況下很少用。
CBC(Cipher Block Chaining):密碼分組鏈接模式。使用一個密鑰和一個初始化向量(IV)對數(shù)據(jù)進行加密。
CBC加密方式,明文被加密前要與前面的密文進行異或運算后再加密,因此只要選擇不同的初始向量,相同的密文加密后會形成不同的密文,這是目前應用最廣泛的模式。CBC加密后的密文是上下文相關的,但明文的錯誤不會傳遞到后續(xù)分組,但如果一個分組丟失,后面的分組將全部作廢(同步錯誤).
CBC可以有效的保證密文的完整性,如果一個數(shù)據(jù)塊在傳遞時丟失或者改變,后面的數(shù)據(jù)無法進行正常的解密。
四、對稱加密算法終端命令
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'回車保存。

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,

再次使用上述命令進行加密,然后輸出一個'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復制代碼
對比如下圖,

六、對稱加密算法代碼演練
接下來開始代碼演練部分,需要導入一個工具類,工具類代碼并不多,這里直接貼工具類的內(nèi)容吧,工具類頭文件AES,DES各種終端命令也都包含在內(nèi)了:
.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? ? 數(shù)據(jù)加密標準? ? */? ? @property (nonatomic, assign) uint32_t algorithm;? ? ? ? /**? ? *? 加密字符串并返回base64編碼字符串? ? *? ? *? @param string? ? 要加密的字符串? ? *? @param keyString 加密密鑰? ? *? @param iv? ? ? ? 初始化向量(8個字節(jié))? ? *? ? *? @return返回加密后的base64編碼字符串? ? */- (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv;? ? ? ? /**? ? *? 解密字符串? ? *? ? *? @param string? ? 加密并base64編碼后的字符串? ? *? @param keyString 解密密鑰? ? *? @param iv? ? ? ? 初始化向量(8個字節(jié))? ? *? ? *? @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;? ? });returninstance;}? ? - (void)setAlgorithm:(uint32_t)algorithm {? ? _algorithm = algorithm;? ? switch (algorithm) {casekCCAlgorithmAES:? ? ? ? self.keySize = kCCKeySizeAES128;? ? ? ? self.blockSize = kCCBlockSizeAES128;break;casekCCAlgorithmDES:? ? ? ? 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;? ? }? ? ? ? // 設置輸出緩沖區(qū)? ? 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(@"[錯誤] 加密失敗|狀態(tài)編碼: %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, 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;? ? }? ? ? ? // 設置輸出緩沖區(qū)? ? 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,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self.algorithm,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? option,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cKey,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self.keySize,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cIv,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [data bytes],? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [data length],? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? buffer,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bufferSize,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &decryptedSize);? ? ? ? NSData *result = nil;if(cryptStatus == kCCSuccess) {? ? ? ? result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];? ? }else{? ? ? ? free(buffer);? ? ? ? NSLog(@"[錯誤] 解密失敗|狀態(tài)編碼: %d", cryptStatus);? ? }return[[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];}? ? ? ? @end復制代碼
接下來新建工程,把工具類.h,.m拖入工程,在ViewController.m實現(xiàn)touchBegin方法
- (void)touchesBegan:(NSSet *)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文件中實現(xiàn),是判斷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)解密的結果是:hello2019-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函數(shù)
第六步已經(jīng)使用過封裝好的加密工具類EncryptionTools.h,這個工具類只是封裝了CCCrypt函數(shù),下面我們來研究一下加密工具的核心函數(shù)CCCrypt函數(shù):
使用CCCrypt函數(shù),需要引入系統(tǒng)庫
#import <CommonCrypto/CommonCrypto.h>復制代碼
不管是加密還是解密都是使用這個函數(shù),下面我們來介紹一下這個函數(shù)中的參數(shù),參數(shù)解釋我直接備注在API的后面,注意里面有坑!?。?/p>
CCCrypt函數(shù)參數(shù)介紹1、 CCOperation op :操作類型:加密or解密,枚舉值;? ? kCCEncrypt 代表加密? ? kCCDecrypt 代表解密2、 CCAlgorithm alg:加密算法,枚舉值;? ? kCCAlgorithmAES? ? 高級加密標準,128位(默認)? ? kCCAlgorithmDES? ? 數(shù)據(jù)加密標準? ? 3、 CCOptions options:加密應用模式,枚舉值;注意注意!?。。。。。。。。∵@里有個坑;kCCOptionPKCS7Padding代表填充模式,這個options必須加上填充模式;? ? CCCrypt的option默認是CBC,所以只需要補充一個填充模式就能代表CBC; 但是ECB就需要額外再加上一個kCCOptionECBMode,所以選擇ECB就需要kCCOptionPKCS7Padding | kCCOptionECBMode;? ? ? ? 所以想要選擇CBC和ECB,需要按下面進行填寫!kCCOptionPKCS7Padding;? 代表CBCkCCOptionPKCS7Padding | kCCOptionECBMode; 代表ECB4、 const void *key :加密的密鑰的指針5、 size_t keyLength:密鑰的長度6、 const void *iv: 初始化向量7、 const void *dataIn:加密的原始數(shù)據(jù)8、 size_t dataInLength:加密的原始數(shù)據(jù)的長度9、 void *dataOut:加密后密文的內(nèi)存地址10、size_t dataOutAvailable:加密后密文的緩沖區(qū)大小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)復制代碼
需要注意的是,直接使用這個函數(shù)安全隱患非常的大!因為這個函數(shù)是系統(tǒng)提供的,不管你是加密還是解密,都是調(diào)用了CCCrypt函數(shù),而黑客可以通過越獄手機附加調(diào)試或者是非越獄手機重簽調(diào)試,能夠用函數(shù)斷點斷到你的CCCrypt函數(shù),然后通過寄存器直接獲取函數(shù)的對應參數(shù),根據(jù)上面函數(shù)的介紹,我們的數(shù)據(jù)其實是其中的第七個參數(shù)'const void *dataIn', 第七個參數(shù)的下標為6, 然后調(diào)用匯編指令'x6',就可以拿到你的數(shù)據(jù)的地址,然后轉一下類型,就能直接打印出你的加密數(shù)據(jù)!具體操作如下:
1、還是打開之前的工程,設置函數(shù)斷點CCCrypt,然后使用真機運行?。?!必須用真機,因為真機和模擬器的CPU不一樣

2、運行工程,模擬黑客調(diào)試,然后點擊屏幕出發(fā)touchBegin方法,然后斷點停在了CCCrypt函數(shù)的地方
3、因為函數(shù)在調(diào)用的時候,都是存在CPU的寄存器上,輸入寄存器查看指令
registerreadx6復制代碼
read x6是讀取該函數(shù)對應的第7個參數(shù),第一個參數(shù)是x0
4、拿到地址,然后強轉類型,蒙圈了吧,你的數(shù)據(jù)就泄漏了

所以這個函數(shù)不能直接使用,現(xiàn)在只說基礎,后面會詳細說安全防護~今天就說到這里了~
最后想要下面的資料可以加小編微信 獲取資料

其實,看萬卷書不如行萬里路,平時直接上手做幾個簡單的項目,然后在項目中學語法,幾者相結合,或許就離大神不遠了
作者: BattleMage