
之前介紹了非對(duì)稱加密算法,這篇文章介紹一下在非對(duì)稱加密算法出現(xiàn)之前的對(duì)稱加密算法,常見的對(duì)稱加密算法、終端演示OpenSSL和iOS代碼運(yùn)用以及CCCrypt的安全隱患等。
對(duì)稱加密算法:明文通過密鑰加密得到密文,密文再通過這個(gè)密鑰解密得到明文。所以在業(yè)務(wù)邏輯上相對(duì)沒有非對(duì)稱加密RSA的安全性高。
常見的對(duì)稱加密算法
- DES
數(shù)據(jù)加密標(biāo)準(zhǔn),但由于強(qiáng)度不高,暴力破解難度不是很高,所以用的很少。 - 3DES
使用3個(gè)密鑰,對(duì)數(shù)據(jù)進(jìn)行三次加密,強(qiáng)度增強(qiáng)。雖然強(qiáng)度相對(duì)DES有所提高,但是對(duì)稱加密算法密鑰的保存就很難,3DES的3個(gè)密鑰更麻煩,所以3DES也沒有被廣泛使用。 - AES
高級(jí)密碼標(biāo)準(zhǔn),加密強(qiáng)度非常高,被廣泛使用,美國安全局和蘋果鑰匙串訪問都是用了AES加密算法。
常用的兩種加密模式
- ECB(Electronic Code Book):電子密碼本模式(每一塊數(shù)據(jù)獨(dú)立加密)
最基本的加密模式,也就是通常理解的加密,相同的明文將永遠(yuǎn)加密成相同的密文,無初始向量,容易受到密碼本重放攻擊,一般情況下很少用。 - CBC(Cipher Block Chaining):密碼分組鏈接模式(使用一個(gè)密鑰和一個(gè)初始化向量[IV]對(duì)數(shù)據(jù)執(zhí)行加密。每一塊數(shù)據(jù)加密都依賴上一塊數(shù)據(jù),有效的保證數(shù)據(jù)的完整性)
明文被加密前要與前面的密文進(jìn)行異或運(yùn)算后再加密,因此只要選擇不同的初始向量,相同的密文加密后會(huì)形成不同的密文,這是目前應(yīng)用最廣泛的模式。CBC加密后的密文是上下文相關(guān)的,但明文的錯(cuò)誤不會(huì)傳遞到后續(xù)分組,但如果一個(gè)分組丟失,后面的分組將全部作廢(同步錯(cuò)誤)。CBC可以有效的保證密文的完整性,如果一個(gè)數(shù)據(jù)塊在傳遞時(shí)丟失或改變,后面的數(shù)據(jù)將無法正常解密。
當(dāng)然了,除了對(duì)稱加密和非對(duì)稱加密外,我們肯定還聽說過Hash
Hash概述
Hash:一般翻譯做“散列”,也有直接音譯為“哈?!钡模褪前讶我忾L度的輸入通過散列算法變換成固定長度的輸出,該輸出就是散列值。這種轉(zhuǎn)換是一種壓縮映射,也就是,散列值的空間通常遠(yuǎn)小于輸入的空間,不同的輸入可能會(huì)散列成相同的輸出,所以不可能從散列值來確定唯一的輸入值。簡單的說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數(shù)
之前介紹了RSA加密算法,在RSA算法之后也衍生出很多加密算法,典型的算法就有HASH函數(shù)(也稱之為散列函數(shù),嚴(yán)格意義上不算是加密算法,只不過是和加密一起用),還有在RSA出現(xiàn)之前的對(duì)稱加密算法,這些算法都是公開的。
Hash特點(diǎn)
算法是公開的
相同的數(shù)據(jù)加密結(jié)果不變
不同的數(shù)據(jù)加密結(jié)果定長(MD5得到的結(jié)果默認(rèn)是128位二進(jìn)制,一般用16進(jìn)制的32個(gè)字符來標(biāo)識(shí))
不可逆
信息摘要,信息“指紋”,用來做數(shù)據(jù)識(shí)別的
HASH用途
密碼加密:服務(wù)器不需要知道用戶真實(shí)密碼,只需要匹配HASH值
搜索引擎
版權(quán)
數(shù)字簽名
OpenSSL演示ECB和CBC的區(qū)別
- OpenSSL演示ECB模式加密
// 先創(chuàng)建一個(gè)待加密的message.txt文件,編輯內(nèi)容
$ vi message.txt
$ cat message.txt
Hello vincent!!!
// enc -des-ecb是對(duì)稱加密算法DES的ECB模式,-K是密鑰,616263就是ASCII碼“abc”,-nosalt不加鹽(OpenSSL默認(rèn)會(huì)加鹽),輸出msg.bin
$ openssl enc -des-ecb -K 616263 -nosalt -in message.txt -out msg.bin
這時(shí)在文件夾內(nèi)會(huì)多出一個(gè)加密過后的二進(jìn)制文件msg.bin,修改message.txt文件的內(nèi)容,再次進(jìn)行加密
// 先修改message.txt的內(nèi)容,vincent->Vincent
$ vi message.txt
$ cat message.txt
Hello Vincent!!!
// 再次對(duì)message.txt進(jìn)行同樣的方式加密,輸出msg1.bin
$ openssl enc -des-ecb -K 616263 -nosalt -in message.txt -out msg1.bin
// 查看下msg.bin和msg1.bin有什么不同
$ xxd msg.bin
00000000: 6d87 4097 d383 0bda a5bc d168 de16 688d m.@........h..h.
00000010: b8db 0794 f9ed eca9
$ xxd msg1.bin
00000000: 20e2 8361 50a7 16a0 a5bc d168 de16 688d ..aP......h..h.
00000010: b8db 0794 f9ed eca9
我們會(huì)發(fā)現(xiàn),修改message.txt內(nèi)容后ECB模式加密的結(jié)果只是修改部分不同,前后加密結(jié)果不變
- OpenSSL演示CBC模式加密
// 先編輯message.txt的內(nèi)容
$ vi message.txt
$ cat message.txt
Hello vincent!!!
// 再次對(duì)message.txt進(jìn)行CBC方式加密,相對(duì)ECB模式除了修改-des-cbc,還會(huì)多一個(gè)iv參數(shù),iv是初始化向量,輸出msg2.bin
$ openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt -in message.txt -out msg2.bin
// 再次編輯message.txt的內(nèi)容,vincent->Vincent
$ vi message.txt
$ cat message.txt
Hello Vincent!!!
// 對(duì)修改后的文件加密,輸出msg3.bin
$ openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt -in message.txt -out msg3.bin
// 查看下msg2.bin和msg3.bin有什么不同
$ xxd msg2.bin
00000000: d647 a33b 0389 dea5 3c81 02c9 ec05 44dd .G.;....<.....D.
00000010: 467c a581 ab1a 415a
$ xxd msg3.bin
00000000: 9882 c1b6 3186 b465 b3be d08a 5ad5 2fd1 ....1..e....Z./.
00000010: 6032 add7 bdb2 07da `2......
經(jīng)多次測(cè)試發(fā)現(xiàn),修改message.txt內(nèi)容后CBC模式加密的結(jié)果是修改部分不同以及后面的加密結(jié)果也會(huì)變化
- 終端測(cè)試指令
加密過程:先加密,再base64編碼
解密過程:先base64解碼,再解密
// 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)加密 128位
$ 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)解密 base64 -D進(jìn)行解碼成二進(jìn)制 -d解密
$ 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
對(duì)稱加密算法代碼演示
// AES加密、ECB模式對(duì)“hello vincent!!!”進(jìn)行加密
NSString *ECBEncryptStr = [[EncryptionTools sharedEncryptionTools] encryptString:@"hello vincent!!!" keyString:@"abc" iv:nil];
NSLog(@"%@", ECBEncryptStr);
// 解密
NSString *ECBDecrypt = [[EncryptionTools sharedEncryptionTools] decryptString:ECBEncryptStr keyString:@"abc" iv:nil];
NSLog(@"%@", ECBDecrypt);
// 一個(gè)數(shù)組,和前面一樣有8個(gè)數(shù)據(jù)
uint8_t iv[8] = {1, 2, 3, 4, 5, 6, 7, 8};
// 把數(shù)組包裝才二進(jìn)制NSdata 把數(shù)組的指針和長度傳進(jìn)去
NSData *ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
// AES加密、CBC模式
NSString *CBCEncryptStr = [[EncryptionTools sharedEncryptionTools] encryptString:@"hello vincent!!!" keyString:@"abc" iv:ivData];
NSLog(@"%@", CBCEncryptStr);
// 解密
NSString *CBCDecrypt = [[EncryptionTools sharedEncryptionTools] decryptString:CBCEncryptStr keyString:@"abc" iv:ivData];
NSLog(@"%@", CBCDecrypt);
打印結(jié)果:
LzWe4b6VMKHECZTg5GEoDvOJyUo3lvcCucS987KliFw=
hello vincent!!!
Vo04z90TAfQX07onyrvCie1SnRpsbHKMkYnaNhcEPP0=
hello vincent!!!
雖然將加密和解密封裝成了兩個(gè)方法,但是蘋果內(nèi)部加密和解密都是用的一個(gè)函數(shù)。先看下其中封裝的一個(gè)方法內(nèi)部實(shí)現(xiàn)
// 加密字符串并返回base64編碼字符串
- (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
// 設(shè)置秘鑰 將keyString轉(zhuǎn)成二進(jìn)制
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; // CBC加密
} else {
option = kCCOptionPKCS7Padding | kCCOptionECBMode; // ECB加密
}
// 設(shè)置輸出緩沖區(qū) 將原始數(shù)據(jù)轉(zhuǎn)成二進(jìn)制,并根據(jù)所使用的加密方式設(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(@"[錯(cuò)誤] 加密失敗|狀態(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(@"[錯(cuò)誤] 解密失敗|狀態(tài)編碼: %d", cryptStatus);
}
return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}
代碼已加入相應(yīng)的注釋,就不解釋代碼了。我們會(huì)發(fā)現(xiàn)蘋果內(nèi)部加密和解密都是用CCCryptorStatus CCCrypt( CCOperation op, CCAlgorithm alg, CCOptions options, const void *key, size_t keyLength, const void *iv, const void *dataIn, size_t dataInLength, void *dataOut, size_t dataOutAvailable, size_t *dataOutMoved),這個(gè)函數(shù)是對(duì)稱加密算法的核心函數(shù)。

WTF!!!11個(gè)參數(shù),這么多,這都是干嘛的?
參數(shù)意義
op:kCCEncrypt(加密)/ kCCDecrypt(解密)
alg:加密算法 kCCAlgorithmAES、kCCAlgorithmDES、kCCAlgorithmBlowfish等等
options:加密方式 kCCOptionPKCS7Padding(CBC方式)/kCCOptionECBMode(ECB方式)
key:加密密鑰
keyLength:密鑰長度
iv:初始化向量 ECB不需要指定(CBC多了這個(gè)參數(shù)就相當(dāng)于加鹽,加密強(qiáng)度更高了)
dataIn:加密的數(shù)據(jù)
dataInLength:加密數(shù)據(jù)的長度
dataOut:緩沖區(qū)(地址),存放密文
dataOutAvailable:緩沖區(qū)的大小
dataOutMoved:加密結(jié)果的大小
搞清楚每個(gè)參數(shù)的意義也就明白了,蘋果這樣設(shè)計(jì)還是挺人性化的。對(duì)稱加密和解密所用的參數(shù)密鑰都是一樣的,所以加密和解密都是用同一個(gè)函數(shù)。蘋果的加密算法也都在CommonCrypto.h這個(gè)庫里面,這個(gè)庫并不在macho中,是在系統(tǒng)中,所以我們大多數(shù)會(huì)認(rèn)為這個(gè)加密會(huì)很安全,但是事實(shí)上并不是這樣。
CCCrypt函數(shù)安全隱患
現(xiàn)在我們已經(jīng)使用CCCrypt對(duì)數(shù)據(jù)進(jìn)行加密和解密了,接下來看下我們用CCCrypt加密的數(shù)據(jù)是否真的安全。下面的內(nèi)容涉及到逆向開發(fā),可能有點(diǎn)跑偏,如果感興趣的小伙伴也可以進(jìn)一步研究一下。
我們加密數(shù)據(jù)就是為了防止中間人攻擊,假設(shè)如果別人拿到我們的APP,別人肯定不會(huì)知道我們的源碼,也不知道在數(shù)據(jù)核心加密的地方是不是用的CCCrypt,這時(shí)別人會(huì)進(jìn)行符號(hào)斷點(diǎn),當(dāng)然這個(gè)只要是沒有去符號(hào),或者系統(tǒng)的都是可以攔截到的。

前方高能!??!
下了斷點(diǎn)后,我們繼續(xù)運(yùn)行剛才的demo,程序果斷進(jìn)入斷點(diǎn),接下來要讀寄存器了?。?!

剛剛說過,CCCrypt第7個(gè)參數(shù)是我們加密的數(shù)據(jù),所以在寄存器中X6(從X0開始)就是我們的加密數(shù)據(jù),我們?cè)趌ldb中讀取X6的地址值,也就是指針,拿到X6的地址值是0x00000001c403ee00,再p (char *)0x00000001c403ee00查看X6的值,回車!??!我們剛剛加密的數(shù)據(jù)顯示出來了,我們?cè)詾楹馨踩氖侄尉瓦@樣被別人拿到了!所以我們有很多核心的加密算法不能直接用。
那怎么防御呢?先想到去符號(hào),前面也說了,這個(gè)庫是系統(tǒng)的,所以沒辦法去符號(hào),當(dāng)然自己實(shí)現(xiàn)或者三方庫,比如支付寶就是直接用的OpenSSL,可以去符號(hào)來避免被直接破解。最好的方式是加密之前不能直接使用關(guān)鍵數(shù)據(jù),我們可以自己對(duì)關(guān)鍵數(shù)據(jù)處理一下比如異或,方法肯定不止一個(gè),如果各位有什么好的解決辦法歡迎交流。
后面可能會(huì)介紹下怎么隱藏函數(shù)調(diào)用,怎樣保護(hù)核心數(shù)據(jù),當(dāng)然逆向大神還是很多的,這也僅僅是讓逆向變的更難而已。該文章為記錄本人的學(xué)習(xí)路程,希望能夠幫助大家,也歡迎大家點(diǎn)贊留言交流?。?!http://www.itdecent.cn/p/a6fca79eb89c