在密碼學中,加密部分主要分爲對稱加密和非對稱加密,非對稱加密主要有RSA非對稱加密(使用公鑰/私鑰來加密解密),對稱加密主要有DES/3DES/AES對稱加密算法,順帶提一下咱們今天介紹的Hash算法,Hash屬於一種消息摘要算法,不屬於加密算法,可是因爲其單向運算,不可逆性,因此Hash是加密算法中的構成部分,Hash算法主要有MD5/Sha1/Sha2,這幾個只是Hash算法加密精度有所不一樣。算法
那麼緊接以前的非對稱加密RSA,直接上此次的乾貨部分安全
一、Hash概述
二、數字簽名
三、對稱加密算法簡介
四、對稱加密算法終端命令
五、對稱加密算法終端演練
六、對稱加密算法代碼演練
七、CCCrypt函數
複製代碼
一、Hash的概念bash
Hash, 通常翻譯爲'散列', 也有直接音譯的'哈希',就是把任意長度的輸入,經過散列算法變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是散列值的空間一般小於輸入的空間,不一樣的輸入可能會散列成相同的輸出,因此不可能從散列值來肯定惟一的輸入值。簡單說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數。函數
二、Hash特色工具
一、算法是公開的
二、對相同的數據運算,獲得的結果是同樣的
三、對於不一樣的數據運算,如MD5(Hash算法的一種)的到的結果默認是128位的,32個字符(16進制標識)
四、無法逆運算(由於哈希值是映射關係,會存在散列碰撞【即:無限個數據加密獲得有限個數據,就存在一個或許多個數據存在一樣的哈希值】)
五、信息摘要,信息’指紋‘,通常是用來作數據識別的(因爲無法作逆運算,因此通常不會用來作加密數據,只是把數據的哈希值取到,而後用來對比,作數據識別的)
複製代碼
三、Hash函數(單向散列函數)測試
一、MD5(Message Digest Algorithm 5)
二、SHA(Secure Hash Algorithm)
SHA又分爲:
SHA-1
SHA-2系列(224,256,384,512,512/224,512/256統稱爲SHA-2系列)
三、MAC(Message Authentication Code)
四、CRC(Cyclic Redundancy Check)
五、SM3(國產哈希算法)
複製代碼
四、Hash用途ui
一、用戶密碼的加密
二、搜索引擎
三、版權
四、數字簽名(應用簽名)
複製代碼
五、HMAC搜索引擎
什麼是HMAC?HMAC(Hash-based message authentication code)是一種使用Hash函數(單向散列函數)來構造消息認證碼的方法,利用哈希算法,以一個密鑰和一個消息爲輸入,生成一個消息摘要做爲輸出。主要是爲了能讓人對對方身份正確性和消息有效性進行驗證,與消息摘要的最大不一樣,就是有簽名密鑰!編碼
HMAC經過兩次hash兩個不一樣的key來生成。 目前尚未發現有任何的方法來產生碰撞。atom
HMAC中所使用的單向散列函數並不只限於一種,任何高強度的Hash函數(單向散列函數)均可以被用於HMAC。 好比使用SHA-一、SHA-22四、SHA-25六、SHA-38四、SHA-512所構造的HMAC,分別稱爲HMAC-SHA一、HMAC-SHA-22四、HMAC-SHA-38四、HMAC-SHA-512。
一、什麼是數字簽名
數字簽名就是用於鑑別數字信息的方法;
二、數字簽名
下面咱們以電商支付金額這個場景來描述數字簽名的具體意義:
圖示中,通過RSA加密的原商品信息Hash值這個總體就叫作數字簽名。
一、對稱加密算法定義:
對稱加密方式:就是明文經過密鑰加密獲得密文。密文經過密鑰解密獲得明文。
二、對稱加密常見算法
一、DES(Data Encryption Standard):數據加密標準,速度較快,適用於加密大量數據的場合;(題外話:實際上用的很少,由於加密強度不夠)
二、3DES(Triple DES):是基於DES的對稱算法,對相同的數據用3個不一樣的密鑰執行3次加密,強度更高;(題外話:不過由於3個密鑰管理起來麻煩,因此通常不是很經常使用~一出生就掛掉了,很慘。。。)
三、RC2和RC4:用變長密鑰對大量數據進行加密,比DES快哦~
四、AES(Advanced Encryption Standard):高級加密標準,是下一代的加密算法標準,速度快,安全級別高,在21世紀AES標準的一個實現是Rijndael算法;(題外話:很安全,蘋果的鑰匙串訪問就是用的AES,美國國家安全局也是用的AES,想要暴力破解基本不可能)
複製代碼
三、對稱加密應用模式 對稱加密主要有兩種應用模式,下面來詳細介紹一下
ECB(Electronic Code Book):電子密碼本模式。每一塊數據, 獨立加密。
ECB是最基本的加密方式,也就是一般理解的加密,相同的明文將永遠加密成相同的密文,無初始向量,容易受到密碼本重放攻擊,通常狀況下不多用。
CBC(Cipher Block Chaining):密碼分組連接模式。使用一個密鑰和一個初始化向量(IV)對數據進行加密。
CBC加密方式,明文被加密前要與前面的密文進行異或運算後再加密,所以只要選擇不一樣的初始向量,相同的密文加密後會造成不一樣的密文,這是目前應用最普遍的模式。CBC加密後的密文是上下文相關的,但明文的錯誤不會傳遞到後續分組,但若是一個分組丟失,後面的分組將所有做廢(同步錯誤).
CBC能夠有效的保證密文的完整性,若是一個數據塊在傳遞時丟失或者改變,後面的數據沒法進行正常的解密。
AES對稱加密算法兩種應用模式下的終端命令分別以下: 一、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
複製代碼
二、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
複製代碼
一、新建一個message.txt文本文件
$ vi message.txt
複製代碼
回車進入編輯界面,點擊'i',進入編輯界面,輸入5排'1234567890',點擊'esc',再點擊'shift+:',輸入'wq'回車保存。
二、對該'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
複製代碼
對好比下圖,
接下來開始代碼演練部分,須要導入一個工具類,工具類代碼並很少,這裏直接貼工具類的內容吧,工具類頭文件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, 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 = [[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(@"[錯誤] 解密失敗|狀態編碼: %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來選取加密方式的
//一、選擇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]);
//二、選擇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
複製代碼
第六步已經使用過封裝好的加密工具類EncryptionTools.h,這個工具類只是封裝了CCCrypt函數,下面咱們來研究一下加密工具的核心函數CCCrypt函數:
使用CCCrypt函數,須要引入系統庫
#import <CommonCrypto/CommonCrypto.h>
複製代碼
無論是加密仍是解密都是使用這個函數,下面咱們來介紹一下這個函數中的參數,參數解釋我直接備註在API的後面,注意裏面有坑!!!
CCCrypt函數
參數介紹
一、 CCOperation op :操做類型:加密or解密,枚舉值;
kCCEncrypt 表明加密
kCCDecrypt 表明解密
二、 CCAlgorithm alg:加密算法,枚舉值;
kCCAlgorithmAES 高級加密標準,128位(默認)
kCCAlgorithmDES 數據加密標準
三、 CCOptions options:加密應用模式,枚舉值;
注意注意!!!!!!!!!!這裏有個坑;kCCOptionPKCS7Padding表明填充模式,這個options必須加上填充模式;
CCCrypt的option默認是CBC,因此只須要補充一個填充模式就能表明CBC; 可是ECB就須要額外再加上一個kCCOptionECBMode,因此選擇ECB就須要kCCOptionPKCS7Padding | kCCOptionECBMode;
因此想要選擇CBC和ECB,須要按下面進行填寫!
kCCOptionPKCS7Padding; 表明CBC
kCCOptionPKCS7Padding | kCCOptionECBMode; 表明ECB
四、 const void *key :加密的密鑰的指針
五、 size_t keyLength:密鑰的長度
六、 const void *iv: 初始化向量
七、 const void *dataIn:加密的原始數據
八、 size_t dataInLength:加密的原始數據的長度
九、 void *dataOut:加密後密文的內存地址
十、size_t dataOutAvailable:加密後密文的緩衝區大小
十一、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',就能夠拿到你的數據的地址,而後轉一下類型,就能直接打印出你的加密數據!具體操做以下:
一、仍是打開以前的工程,設置函數斷點CCCrypt,而後使用真機運行!!!必須用真機,由於真機和模擬器的CPU不同
二、運行工程,模擬黑客調試,而後點擊屏幕出發touchBegin方法,而後斷點停在了CCCrypt函數的地方
三、由於函數在調用的時候,都是存在CPU的寄存器上,輸入寄存器查看指令
register read x6
複製代碼
read x6是讀取該函數對應的第7個參數,第一個參數是x0
四、拿到地址,而後強轉類型,蒙圈了吧,你的數據就泄漏了
因此這個函數不能直接使用,如今只說基礎,後面會詳細說安全防禦~今天就說到這裏了~