在密碼學中,加密部分主要分為對稱加密和非對稱加密,非對稱加密主要有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, 一般翻譯為'散列', 也有直接音譯的'哈希',就是把任意長度的輸入,通過散列算法變換成固定長度的輸出,該輸出就是散列值。這種轉(zhuǎn)換是一種壓縮映射,也就是散列值的空間通常小于輸入的空間,不同的輸入可能會散列成相同的輸出,所以不可能從散列值來確定唯一的輸入值。簡單說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數(shù)。
2、Hash特點
1、算法是公開的
2、對相同的數(shù)據(jù)運算,得到的結(jié)果是一樣的
3、對于不同的數(shù)據(jù)運算,如MD5(Hash算法的一種)的到的結(jié)果默認是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;
});
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;
}
// 設置輸出緩沖區(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<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文件中實現(xiàn),是判斷iv是否為nil來選取加密方式的
//1、選擇AES(ECB)
NSString * encStr = [[EncryptionTools sharedEncryptionTools]encryptString:@"hello" keyString:key iv:nil];
NSLog(@"AES(ECB)加密的結(jié)果是:%@", encStr);
NSLog(@"AES(ECB)解密的結(jié)果是:%@", [[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)加密的結(jié)果是:%@", encStr1);
NSLog(@"AES(CBC)解密的結(jié)果是:%@", [[EncryptionTools sharedEncryptionTools]decryptString:encStr1 keyString:key iv:ivData]);
}
@end
點擊運行,運行結(jié)果OK
2019-10-12 21:57:26.858675+0800 CryptDemo[1790:115503] AES(ECB)加密的結(jié)果是:d1QG4T2tivoi0Kiu3NEmZQ==
2019-10-12 21:57:26.858896+0800 CryptDemo[1790:115503] AES(ECB)解密的結(jié)果是:hello
2019-10-12 21:57:26.859040+0800 CryptDemo[1790:115503] AES(CBC)加密的結(jié)果是:u3W/N816uzFpcg6pZ+kbdg==
2019-10-12 21:57:26.859194+0800 CryptDemo[1790:115503] AES(CBC)解密的結(jié)果是: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的后面,注意里面有坑!!!
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; 代表CBC
kCCOptionPKCS7Padding | kCCOptionECBMode; 代表ECB
4、 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 :加密結(jié)果的大小
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ù)的地址,然后轉(zhuǎn)一下類型,就能直接打印出你的加密數(shù)據(jù)!具體操作如下:
1、還是打開之前的工程,設置函數(shù)斷點CCCrypt,然后使用真機運行!!!必須用真機,因為真機和模擬器的CPU不一樣
2、運行工程,模擬黑客調(diào)試,然后點擊屏幕出發(fā)touchBegin方法,然后斷點停在了CCCrypt函數(shù)的地方
3、因為函數(shù)在調(diào)用的時候,都是存在CPU的寄存器上,輸入寄存器查看指令
register read x6
read x6是讀取該函數(shù)對應的第7個參數(shù),第一個參數(shù)是x0
4、拿到地址,然后強轉(zhuǎn)類型,蒙圈了吧,你的數(shù)據(jù)就泄漏了
所以這個函數(shù)不能直接使用,現(xiàn)在只說基礎,后面會詳細說安全防護今天就說到這里了