Hash
Hash,一般翻譯做散列、雜湊,或音譯為哈希,是把任意長度的輸入(又叫做預(yù)映射pre-image)通過散列算法變換成固定長度的輸出,該輸出就是散列值。這種轉(zhuǎn)換是一種壓縮映射,也就是,散列值的空間通常遠小于輸入的空間,不同的輸入可能會散列成相同的輸出,所以不可能從散列值來確定唯一的輸入值。簡單的說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數(shù)。
常用Hash函數(shù)
MD5
SHA-1
SHA2(SHA2包括了SHA-224、SHA-256、SHA-384,和SHA-512)
Hash的特點
- 算法是公開的
- 對相同的數(shù)據(jù)運算后,得到的結(jié)果是一樣的
- 對不同數(shù)據(jù)運算,如MD5得到的結(jié)果默認是128位(0x - 32byte)
- Hash是不可逆運算
- 信息摘要,信息“指紋”,用于做數(shù)據(jù)的識別匹配
因為Hash的算法以及特性,Hash結(jié)果有“必然存在,偶然出現(xiàn)”的散列碰撞
Hash的用途
- 搜索引擎
- Rabin-Karp字符串搜索算法 是一個相對快速的字符串搜索算法,它所需要的平均搜索時間是O(n).這個算法是創(chuàng)建在使用散列來比較字符串的基礎(chǔ)上的。
- 用戶密碼的加密
- 一般服務(wù)器不需要知道用戶的真是密碼,而是通過Hash匹配
- 如果直接使用MD5加密,容易被反查詢
- 加鹽,不使用固定的加鹽,提高安全性
- HMAC加密方案:通過動態(tài)Key來加密
- 加時間戳的方式,靈活,可保證加密結(jié)果每次不同,增加時效性。
- 數(shù)字簽名
- 對傳輸?shù)脑瓟?shù)據(jù)進行Hash
- 使用RSA加密Hash值(這部分數(shù)據(jù)就是原始數(shù)據(jù)的簽名信息)
- 將 原始數(shù)據(jù)+數(shù)字簽名 一起發(fā)送給服務(wù)器驗證
- 版權(quán)
- 文件檢驗【針對文件二進制的hash】
用戶密碼加密使用
- 安全的傳遞密碼信息
- 服務(wù)器不需要保存用戶的真實密碼
使用方式/使用弊端
1、直接對密碼進行MD5的Hash,容易被散列碰撞庫查詢出來
2、加鹽方式,提高了一定的安全性,但是salt是固定的,一但泄露,就不安全了
3、HMAC加密方案,使用一個Key,并且做了兩次散列,實際開發(fā)中,Key來自于服務(wù)器【相對安全】
針對HMAC加密方案做一個安全流出
- 在注冊的時候,服務(wù)器針對用戶生成一個對應(yīng)的Key
- 注冊時,服務(wù)器保存的是用戶HMAC加密后的密碼
- 多設(shè)備操作:如果用戶在新設(shè)備上登錄,需要獲取登錄權(quán)限(Key),服務(wù)器在接收請求時
- 如果沒有開啟權(quán)限鎖,服務(wù)器直接返回用戶對應(yīng)的權(quán)限(Key)
- 如果開啟了權(quán)限鎖,需要已經(jīng)登錄的設(shè)備上請求是否給予權(quán)限(Key)
- 針對第三方截獲密碼的Hash值
- 客戶端在進行登錄操作時,追加服務(wù)器的時間戳進行Hash
- 服務(wù)器在檢驗時,最多進行兩次的匹配方式
例如
客戶端請求 ((HMAC哈希值)+“201910081801”).MD5
服務(wù)器驗證 ((HMAC哈希值)+“201910081802”).MD5,如果不匹配再驗證((HMAC哈希值)+“201910081801”).MD5【存在59秒的時候發(fā)起的請求,服務(wù)器接收后時間過了】- 這樣使得請求的Hash校驗有一定的時效安全
對稱加密
采用單鑰密碼系統(tǒng)的加密方法,同一個密鑰可以同時用作信息的加密和解密,這種加密方法稱為對稱加密,也稱為單密鑰加密。
- 需要對加密和解密使用相同密鑰的加密算法。由于其速度快,對稱性加密通常在消息發(fā)送方需要加密大量數(shù)據(jù)時使用。對稱性加密也稱為密鑰加密。
- 所謂對稱,就是采用這種加密方法的雙方使用方式用同樣的密鑰進行加密和解密。密鑰是控制加密及解密過程的指令。算法是一組規(guī)則,規(guī)定如何進行加密和解密。
- 因此加密的安全性不僅取決于加密算法本身,密鑰管理的安全性更是重要。因為加密和解密都使用同一個密鑰,如何把密鑰安全地傳遞到解密者手上就成了必須要解決的問題。
常見算法
常用的單向加密算法:
- DES(Data Encryption Standard):數(shù)據(jù)加密標準,速度較快,適合用于加密大量數(shù)據(jù)
- 3DES(Triple DES):是基于DES,對一塊數(shù)據(jù)用3個不同的密鑰進行3次加密,強化了安全性
- AES(Advanced Encryption Standard):高級加密標準,速度快,安全級別高,支持128,192,256,512位密鑰的加密
算法特征
- 加密方和解密方使用同一個密鑰;
- 加密解密的速度比較快,適合數(shù)據(jù)比較長時的使用;
- 密鑰傳輸?shù)倪^程不安全,且容易被破解,密鑰管理也比較麻煩;
優(yōu)缺點
優(yōu)點:算法公開,計算量小,加密速度快,加密效率高
缺點:
- 在數(shù)據(jù)傳遞錢,發(fā)送發(fā)和接收方需要商定好密鑰,然后保存好密鑰。
- 如果有一方的密鑰泄露,那么信息就不安全了。
- 對于安全性,每對用戶擁有自己的獨立密鑰,這使得收發(fā)雙方會擁有大量的密鑰,讓管理成為負擔(dān)。
AES應(yīng)用
- ECB(Electronic Code Book):電子密碼本模式。
- 每塊數(shù)據(jù),獨立加密(待處理的信息被分成大小合適的分組,然后分別對每一分組獨立進行加密或解密處理)。
- 這是最基本的加密模式,相同的明文將永遠加密成相同的密文,無初始化向量,容易受到密碼本重放攻擊,一般情況下用于小數(shù)據(jù)量的字符信息的安全性保護,例如密鑰保護。
- CBC(Cipher Block Chaining):密碼分組鏈接模式。
- 使用一個密鑰和一個初始化向量[IV]對數(shù)據(jù)進行加密。
- 明文被加密前要與前面的密文進行異或運算后在加密,因此只要選擇不同的初始化向量,相同的明文加密后不會出現(xiàn)相同的密文,是目前使用最廣泛的模式。
- CBC可以有效的保證密文的完整性,如果一塊數(shù)據(jù)塊在傳遞中丟失或者改變,后續(xù)的數(shù)據(jù)將無法正常的解密。
OpenSSL 終端指令
加密
- AES(ECB)加密字符串
$ echo -n string | openssl enc - aes-128-ecb -K 616263 -nosalt | base64- AES(CBC)加密字符串
$ echo -n string | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt | base64
解密
- AES(ECB)解密
$ echo -n 需要解密的base64 | base64 -D | openssl enc -aes-128-ecb -K 616263 -nosalt -d- AES(CBC)解密
$ echo -n 需要解密的base64 | base64 -D | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -d
CCCrypt函數(shù)的使用
- import <CommonCrypto/CommonCrypto.h>
- 加密解密的算法都是使用到CCCrypt這個函數(shù)
Parameter:
CCOperation op > KCCEncrypt加密 / KCCDecrypt 解密
CCAlgorithm alg > 加密算法【kCCAlgorithm 枚舉】
CCOptions options > 加密選項:ECB/CBC
const void *key > 加密的密鑰
size_t keyLength > 密鑰的長度
const void *iv > iv 初始化向量
const void *dataIn > 要被加密的數(shù)據(jù)
size_t dataInLength > 要被加密數(shù)據(jù)的長度
void *dataOut > 密文的內(nèi)存地址
size_t dataOutAvailable > 密文緩沖區(qū)的大小
size_t *dataOutMoved > 解密結(jié)果大小
- (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
// 設(shè)置秘鑰
NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
uint8_t cKey[self.keySize];
bzero(cKey, sizeof(cKey));
[keyData getBytes:cKey length:self.keySize];
// 設(shè)置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;
}
// 設(shè)置輸出緩沖區(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 {
// 設(shè)置秘鑰
NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
uint8_t cKey[self.keySize];
bzero(cKey, sizeof(cKey));
[keyData getBytes:cKey length:self.keySize];
// 設(shè)置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;
}
// 設(shè)置輸出緩沖區(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];
}