1. 密碼學(xué)發(fā)展簡介
密碼學(xué)是指研究信息加密,破解密碼的技術(shù)科學(xué)。密碼學(xué)的起源可追溯到2000年前。而密碼學(xué)是以數(shù)字為基礎(chǔ)的。
發(fā)展歷史
-
密碼學(xué)的歷史大致可以追溯到兩千年前,相傳古羅馬名將凱撒大帝為了防止敵方截獲情報(bào),用密碼傳送情報(bào)。凱撒的做法很簡單,就是對二十幾個羅馬字母建立一張對應(yīng)表。這樣,如果不知道密碼本,即使截獲一段信息也看不懂。
image.png 從凱撒大帝時代到上世紀(jì)70年代這段很長的時間里,密碼學(xué)的發(fā)展非常的緩慢,因?yàn)樵O(shè)計(jì)者基本上靠經(jīng)驗(yàn)。沒有運(yùn)用數(shù)學(xué)原理
在1976年以前,所有的加密方法都是同一種模式:加密、解密使用同一種算法。在交互數(shù)據(jù)的時候,彼此通信的雙方就必須將規(guī)則告訴對方,否則沒法解密。那么加密和解密的規(guī)則(簡稱密鑰),它保護(hù)就顯得尤其重要。傳遞密鑰就成為了最大的隱患。這種加密方式被成為對稱加密算法(symmetric encryption algorithm)
-
1976年,兩位美國計(jì)算機(jī)學(xué)家 迪菲(W.Diffie)、赫爾曼(M.Hellman) 提出了一種嶄新構(gòu)思,可以在不直接傳遞密鑰的情況下,完成密鑰交換。這被稱為“迪菲赫爾曼密鑰交換”算法。開創(chuàng)了密碼學(xué)研究的新方向
image.png
2. 非對稱加密RSA產(chǎn)生過程
- 上世紀(jì)70年代產(chǎn)生的一種加密算法。其加密方式比較特殊,需要兩個密鑰:公開密鑰簡稱公鑰(
publickey)和私有密鑰簡稱私鑰(privatekey)。公鑰加密,私鑰解密;私鑰加密,公鑰解密。這個加密算法就是偉大的RSA - 這種算法非??煽?,密鑰越長,它就越難破解。根據(jù)已經(jīng)披露的文獻(xiàn),目前被破解的最長 RSA密鑰是 768 個二進(jìn)制位。也就是說,長度超過 768 位的密鑰,還無法破解(至少沒人公開宣布)。因此可以認(rèn)為,1024 位的 RSA 密鑰基本安全,2048 位的密鑰極其安全。
( 當(dāng)然 RSA 的缺陷也很容易想到 : 效率相對較低 , 字節(jié)長度限制等 . 因此實(shí)際應(yīng)用中我們往往會結(jié)合對稱性加密一起使用 , 關(guān)鍵內(nèi)容使用 RSA )
3. RSA 數(shù)學(xué)原理
3.1 離散對數(shù)問題
3.1.1 原根
-
先從一個問題開始:三的多少次方模 17 等于 12?
image.png
- 顯然 , 對于離散對數(shù)問題 , 其正向計(jì)算得到右邊 12 很簡單. 但是反向運(yùn)算時 , 就無從下手. 只能窮舉 .
- 而且當(dāng)模數(shù)使用質(zhì)數(shù) 17 時 , 結(jié)果固定在 1 ~ 17 之間. 而當(dāng) 17 這個模數(shù)足夠大時 , 就算知道采用的是這個算法 , 也知道 17 這個質(zhì)數(shù)和答案 , 想要再計(jì)算出來上圖中這個問號值 , 可以想象到其難度和計(jì)算量有多大 .
-
如下圖:
image.png - 從上圖我們可以看出,3的N次方取模17的結(jié)果是范圍:從1到16的任意一個數(shù)字。這樣3稱為17的原根。
-
這樣我們得出一個規(guī)律用來加密,3 的 N次方,也就是上面圖的
image.png
的問號,我們可以用作N(公式中的?號)作為明文,得到密文 12 。這樣即使黑客得到我們在網(wǎng)絡(luò)中傳輸?shù)拿芪?2 ,就是他知道這個公式,他也很難反算出我們的明文 N。特別是我們把被模數(shù)17改的更大一些,如改為幾百位的數(shù)字,那么黑客基本上是不可能通過這個公式反算出我們的明文的。他只能通過不斷試錯的暴力破解方式。
- 通過上面這個公式反算,計(jì)算出明文N的問題叫做離散對數(shù)問題
3.2 歐拉函數(shù)Φ
先了解一些概念
- 關(guān)于互質(zhì)關(guān)系:如果兩個正整數(shù),除了1以外,沒有其他公因數(shù),我們就稱這兩個數(shù)是互質(zhì)關(guān)系(coprime)。
如果一個數(shù)N是質(zhì)數(shù),那么小于N的數(shù)都會與N 這個數(shù)字互為質(zhì)數(shù)。如N=5,那么1,2,3,4都與5構(gòu)成互質(zhì)關(guān)系,那么 Φ(5) = 4,表示有4個數(shù)與5構(gòu)成互質(zhì)關(guān)系。
- 任意給定正整數(shù)n,請問在小于等于n的正整數(shù)之中,有多少個與n構(gòu)成互質(zhì)關(guān)系?計(jì)算這個值的方式叫做歐拉函數(shù),使用:Φ(n)表示
- 如: 計(jì)算8的歐拉函數(shù),和8互質(zhì)的 1、2、3、4、5、6、7、8,Φ(8) = 4
- 計(jì)算7的歐拉函數(shù),和7互質(zhì)的 1、2、3、4、5、6、7,Φ(7) = 6
- 計(jì)算56的歐拉函數(shù),Φ(56) = Φ(8) * Φ(7) = 4 * 6 = 24
通過上面的一些推理,我們不難發(fā)現(xiàn)歐拉函數(shù)的特點(diǎn):
- 歐拉函數(shù)特點(diǎn)
- 當(dāng)n是質(zhì)數(shù)的時候,Φ(n)=n-1。
- 如果n可以分解成兩個互質(zhì)的整數(shù)之積,如n=AB則:Φ(AB)=Φ(A)*Φ(B)
- 根據(jù)以上兩點(diǎn)得到:如果N是兩個質(zhì)數(shù)P1 和 P2的乘積則: Φ(N)=Φ(P1)Φ(P2)=(P1-1)(P2-1)
例如 15 = 3 * 5 ,Φ(53)=Φ(5)Φ(3) , 而 Φ(5) = 4,Φ(3) = 2, 則Φ(53)=Φ(5)Φ(3) = 4 * 2 = 8, 也就是15有8個數(shù)與它構(gòu)成互質(zhì)關(guān)系。
3.3 歐拉定理
-
歐拉定理:如果兩個正整數(shù)m和n互質(zhì),那么m的Φ(n)次方減去1,可以被n整除。(m^Φ(n)-1)/n = K(整數(shù))
image.png -
費(fèi)馬小定理:歐拉定理的特殊情況:如果兩個正整數(shù)m和n互質(zhì),而且n為質(zhì)數(shù)!那么Φ(n)結(jié)果就是n-1。(m^(n-1)-1)/n = K(整數(shù))
image.png
3.4 公式轉(zhuǎn)換
模反元素:如果兩個正整數(shù)e和x互質(zhì),那么一定可以找到整數(shù)d,使得 ed-1 被x整除。那么d就是e對于x的“模反元素”

如上圖所示,轉(zhuǎn)換過程5步即可:
-
首先根據(jù)歐拉定理
image.png -
由于 1 的 k 次方恒等于 1 , 那么
image.png -
由于 1*m ≡ m , 那么
image.png -
用模反元素轉(zhuǎn)換,那么換算成公式 就是:
image.png -
轉(zhuǎn)換一下寫法
image.png -
比較第五步和第三步中紅框部分. 也就是說當(dāng) x 等于 Φ(n) 時 :
image.png
d 是 e 相對于 φ(n) 的模反元素
注意 : 公式推導(dǎo)第一步時 我們歐拉定理的前提是 m 和 n 互質(zhì) , 但是由于模反元素的關(guān)系 , 其實(shí)只要滿足 m < n 上述結(jié)果依然成立.
如果上面的這個公式可以拆分為兩次,就可以用來加密。
- 我們可以在終端使用python來驗(yàn)證一下:
M = 4, N = 15, φ(n) = 8, e = 3,
d ? 3d -1 = 8
d = (8k+1)/3 -> ( 3, 11)
這里我們可以取d = 11
image.png
上面驗(yàn)證知道,m,n不一定要,只需要m < n即可。

從終端打印結(jié)構(gòu)可以看出:n = 15 只要 m < n 也就是 m <= 14 無論是否是質(zhì)數(shù),公式:

都成立。
- 然而科學(xué)家們一直停留在這個公式階段,直到迪菲赫爾曼密鑰交換出現(xiàn),通過拆分這個公式實(shí)現(xiàn)。
3.5 迪菲赫爾曼密鑰交換
-
實(shí)際場景來看下迪菲赫爾曼密鑰交換過程如下圖:
image.png - 客戶端先選一個隨機(jī)數(shù)13 ,這個數(shù)除了客戶端知道,沒有其他任何人知道。
- 服務(wù)器選一個隨機(jī)數(shù)15, 這個數(shù)字除了服務(wù)器端,沒有任何知道。
- 這兩個數(shù)字13,15分別只有客戶端和服務(wù)器自己知道,不會在網(wǎng)絡(luò)上傳輸,所以不會被泄密。
- 客戶端用3作為根原, 3 的13次方 然后取模 17 (3^13mod 17 = 12),得到12,發(fā)給服務(wù)器端。
- 服務(wù)器端拿到12后,先將12保存起來,服務(wù)器端用同客戶端一樣的算法(3^15mod17 = 6),得到數(shù)字6,發(fā)給客戶端。
- 這樣客戶端和服務(wù)器端就完成了彼此的密鑰交換。
- 然后客戶端和服務(wù)器分別做如下一次運(yùn)算:
- 客戶端拿到服務(wù)器發(fā)過來的數(shù)字6,用同樣的算法,(6^13mod17 = 10), 服務(wù)器端用從客戶端拿到的數(shù)字12,用同樣的算法(12^ 15mod 17 = 10)同樣也是得到10,這個10 就是客戶端和服務(wù)器交換的秘鑰。
- 這樣網(wǎng)絡(luò)上從來就沒有傳輸過秘鑰10,而客戶端和服務(wù)器卻通過同樣的算法,計(jì)算兩次就得到了密鑰。
3.5.1 數(shù)學(xué)原理
-
上面講解的迪菲赫爾曼密鑰交換的數(shù)學(xué)原理如下圖:
image.png - 實(shí)際上客戶端和服務(wù)器都做了兩次運(yùn)算,
- 客戶端的兩次運(yùn)算:
- 第一次是服務(wù)器端做的運(yùn)算:3^15mod 17 = 6
- 第二次是客戶端自己拿到服務(wù)器端的6繼續(xù)做的一次運(yùn)算:6^13 mod17 = 10
- 第二次運(yùn)算的6 用第一次的315替換就實(shí)際上得到:315^13 mod17 = 10
- 服務(wù)器端的兩次運(yùn)算:
- 第一次是在客戶端做的運(yùn)算:3^13mod17 = 12
- 第二次是拿到客戶端的12繼續(xù)做一次運(yùn)算:12^15mod17 = 10
- 第二次運(yùn)算的12實(shí)際上是用313代替:313^15 mod 17 = 10
- 這樣我們可以清楚的看到:客戶端(31513 mod17 = 10)= 服務(wù)器(31315 mod 17 = 10)
-
那我們把上面的計(jì)算過程總結(jié)出來就是如下的公式:
image.png
上面的計(jì)算套用公式:
如上面服務(wù)器端的計(jì)算: m=3, e=13, n=17, C=12 (運(yùn)算公式:3^13mod17 = 12)
實(shí)際上就是:m^e mod n = C
然后由于d = 15, (運(yùn)算公式:12^15 mod 17 = 10)
實(shí)際上就是: C^d mod n = m ,由于 C = m ^ e mod n,可以得到 m ^ e ^ d mod n = m, 也就是:m ^ (ed) mod n = m
實(shí)際上就是對ed 進(jìn)行了拆分,拆分成了兩次運(yùn)算。
-
結(jié)合我們剛剛第五步之后得出的
image.png -
拆分公式,可以用來加密,解密還原數(shù)據(jù):
image.png
其中 d 是 e 相對于 φ(n) 的模反元素 , 因?yàn)?x = Φ(n) , 那么同樣 , e 和 φ(n) 是互質(zhì)關(guān)系
- 舉例驗(yàn)證:例如: m = 3 , n = 15 , φ(n) = 8 , e = 3 , d = 11
通過終端python3驗(yàn)證:

-
總結(jié)如圖:
image.png
3.7 RSA的誕生
- 由上面的迪菲赫爾曼密鑰交換 和我們得出的公式:m ^ (e*d) mod n = m ,兩者結(jié)合換算,可以得到加密和解密的公式:
- 加密: m ^ e mod n = c, (c 加密的結(jié)果,m是明文, e和n就是公鑰,d和n就是私鑰)
- 解密:c ^ d mod n = m
-
公式換算如下圖:
image.png
- n 會非常大,長度一般為 1024 個二進(jìn)制位。(目前人類已經(jīng)分解的最大整數(shù),232 個十進(jìn)制位,768 個二進(jìn)制位)
- 由于需要求出 φ(n),所以根據(jù)歐函數(shù)特點(diǎn),最簡單的方式 n 由兩個質(zhì)數(shù)相乘得到: 質(zhì)數(shù):p1、p2 . 那么
Φ(n) = (p1 -1) * (p2 - 1)- 最終由 φ(n) 得到 e 和 d 。
總共生成 6 個數(shù)字:p1、p2、n、φ(n)、e、d
其中 n 和 e 組成公鑰 .
n 和 d 組成私鑰 .
m 為明文 .
c為密文 .- 除了公鑰用到了 n 和 e 其余的 4 個數(shù)字是不公開的。
3.8 RSA算法
只要滿足d是e相對于Φ(n)的模反元素
-
m小于n
image.png 下面我們通過python來驗(yàn)證一下:
m ^ e mod n = c 加密
c ^ d mod n = m 解密
我們假設(shè) n = 15 則 φ(n) = φ(15) = 8,
假設(shè) e = 3
假設(shè) d= 19
假設(shè)明文 m = 7
先來計(jì)算出加密:c = 7 ^ 3 mod 15 = 13
然后解密:13 ^ 19 mod 15 = 7

- RSA算法的特點(diǎn):
- 總共生成 6 個數(shù)字:p1、p2、n、φ(n)、e、d
其中 n 和 e 組成公鑰 .
n 和 d 組成私鑰 .
m 為明文 .
c為密文 .- 除了公鑰用到了 n 和 e 其余的 4 個數(shù)字是不公開的。
- 黑客要破解實(shí)際上就是根據(jù)n, 去求φ(n), 而當(dāng)n比較大時,是很難算出φ(n),φ(n)只能通過試錯的方式去暴力破解(用因式分解方式)。
- 要求出φ(n) 目前最大只能計(jì)算到232個十進(jìn)制位,只是運(yùn)算時間的問題,如果量子計(jì)算機(jī)真的出來了,因?yàn)榱孔佑?jì)算理論上運(yùn)算量是無窮大的,所以可以破解這個φ(n),由于銀行等很多大公司都是用的RSA加密方式,所以量子計(jì)算的問世,將會對密碼學(xué)產(chǎn)生很大的影響。
3.9 終端演練RSA加密算法
-
Mac的終端可以直接使用OpenSSL進(jìn)行RSA的命令運(yùn)行。
image.png OpenSSL使用RSA
- 生成RSA私鑰,秘鑰長度為1024bit
終端輸入命令:openssl genrsa -out private.pem 1024
image.png- 從私鑰中提取公鑰
終端輸入命令:openssl rsa -in private.pem -pubout -out public.pem
image.png通過上面兩步分別已經(jīng)生成了公鑰,私鑰文件
image.png我們查看一下生成的公鑰,私鑰是什么東東
image.png查看一下公鑰內(nèi)容:
image.png- 實(shí)際上公鑰,私鑰都是經(jīng)過base64加密的,我們接下來將私鑰轉(zhuǎn)換成明文查看:
終端輸入命令:openssl rsa -in private.pem -text -out private.txt
image.png我們查看一下私鑰的明文:
終端輸入:cat private.txt
image.png
3.9.1 openssl實(shí)現(xiàn)rsa加密 ,解密
- 打開終端,新建一個message.txt文件:
vi message.txt -
輸入hello,保存
image.png - 通過公鑰進(jìn)行加密:終端輸入:
openssl rsautl -encrypt -in message.txt -inkey public.pem -pubin -out enc.txt

加密后的內(nèi)容hello變成了亂碼了。
- 通過私鑰進(jìn)行解密,終端輸入:
penssl rsautl -decrypt -in enc.txt -inkey private.pem -out dec.txt

解密后在dec.txt輸出了原來的明文hello
- 此外我們還可以用私鑰進(jìn)行加密,公鑰進(jìn)行解密。
- 私鑰通過sign進(jìn)行私鑰加密
- 終端輸入命令:
penssl rsautl -sign -in message.txt -inkey private.pem -out enc.bin

- 然后我們用公鑰進(jìn)行解密,終端輸入:
openssl rsautl -verify -in enc.bin -inkey public.pem -pubin -out dec.txt
解密到dec.txt ,我們可以看到解密后的明文也還原了hello

3.9.2 openssl 提取證書p12文件
- rsa 由于效率不高,不太適合大的數(shù)據(jù)加密,一般用來加密關(guān)鍵數(shù)據(jù),如交換秘鑰用rsa加密,rsa也經(jīng)常用于加密hash值,也就是我們所說的簽名。
- 在代碼里面加密我們一般不會直接使用pem文件,一般要提前證書文件
- 在終端輸入:
openssl req -new -key private.pem -out rsacert.csr
會生成一個.csr文件
其中按提示輸入一些信息,如郵箱,密碼等

- csr文件實(shí)際上會去請求一個證書文件,向證書頒發(fā)機(jī)構(gòu)頒發(fā)一個證書。
- 頒發(fā)證書終端命令:
openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt

-
這樣我們就得到了頒發(fā)的證書rsacert.crt文件:
image.png 這個頒發(fā)(官方認(rèn)證,證書結(jié)構(gòu)蓋章的)的證書是要收費(fèi)的,機(jī)構(gòu)一般要收5千元一年,上面我們寫的有效期是10年,意味著要交5萬元,o my gad.
這個證書我們不會直接使用,還需要提前
終端輸入命令:
openssl x509 -outform der -in rsacert.crt -out rsacert.der

-
提取到文件rsacert.der
image.png - 這個文件主要包含公鑰和一些必要信息,后面我們就通過這個der生成一個p12文件,
- p12文件實(shí)際上就包含公鑰和私鑰。
- 接下來,我們到處p12文件。
- 終端輸入命令
openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
-
這個時候會提示我們輸入密碼,如下圖:
image.png -
輸入密碼后(需要確認(rèn)兩次密碼)
image.png -
這樣我們就提前到了p12文件
image.png -
實(shí)際上我們就可以用p.p12 和 rsacert.der進(jìn)行加密和解密
image.png
3.9.3 終端base64編碼
- 我們在rsa文件夾下面有一張kyl.jpg圖片,現(xiàn)在通過終端進(jìn)行base64編碼

-
先終端cd 到rsa這個目錄
image.png 終端輸入編碼命令:
base64 kyl.jpg -o pic.txt

- 現(xiàn)在我們可以用終端進(jìn)行base64解碼:
base64 pic.txt -o 123.png -D

解碼后我們得到123.png圖片

3.9.4 base64編碼代碼實(shí)現(xiàn)
//給一個字符 編碼
-(NSString *)base64Endcode:(NSString *)str{
NSData * data = [str dataUsingEncoding:NSUTF8StringEncoding];
return [data base64EncodedStringWithOptions:0];
}
//給一個編碼我對其進(jìn)行解密
-(NSString *)base64Decode:(NSString *)str{
NSData * data = [[NSData alloc] initWithBase64EncodedString:str options:0];
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
-
base64 簡介
image.png
4. RSA加密代碼實(shí)現(xiàn)
4.1 RSA加密代碼下載
- RSA加密代碼:點(diǎn)擊下載RSA加密代碼
4.2加密代碼講解
4.2.1 新建一個KRSACryptor單例類
KRSACryptor.h文件如下:
//
// KRSACryptor.h
// 001-KylAppEncrypt
//
// Created by 孔雨露 on 2019/12/14.
// Copyright ? 2019 Apple. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface KRSACryptor : NSObject
+ (instancetype)shared;
/**
* 生成密鑰對
*
* @param keySize 密鑰尺寸,可選數(shù)值(512/1024/2048)
*/
- (void)generateKeyPair:(NSUInteger)keySize;
/**
* 加載公鑰
*
* @param publicKeyPath 公鑰路徑
*
@code
# 生成證書
$ openssl genrsa -out ca.key 1024
# 創(chuàng)建證書請求
$ openssl req -new -key ca.key -out rsacert.csr
# 生成證書并簽名
$ openssl x509 -req -days 3650 -in rsacert.csr -signkey ca.key -out rsacert.crt
# 轉(zhuǎn)換格式
$ openssl x509 -outform der -in rsacert.crt -out rsacert.der
@endcode
*/
- (void)loadPublicKey:(NSString *)publicKeyPath;
/**
* 加載私鑰
*
* @param privateKeyPath p12文件路徑
* @param password p12文件密碼
*
@code
openssl pkcs12 -export -out p.p12 -inkey ca.key -in rsacert.crt
@endcode
*/
- (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password;
/**
* 加密數(shù)據(jù)
*
* @param plainData 明文數(shù)據(jù)
*
* @return 密文數(shù)據(jù)
*/
- (NSData *)encryptData:(NSData *)plainData;
/**
* 解密數(shù)據(jù)
*
* @param cipherData 密文數(shù)據(jù)
*
* @return 明文數(shù)據(jù)
*/
- (NSData *)decryptData:(NSData *)cipherData;
@end
NS_ASSUME_NONNULL_END
KRSACryptor.m文件如下:
//
// KRSACryptor.m
// 001-KylAppEncrypt
//
// Created by 孔雨露 on 2019/12/14.
// Copyright ? 2019 Apple. All rights reserved.
//
#import "KRSACryptor.h"
// 填充模式
#define kTypeOfWrapPadding kSecPaddingPKCS1
// 公鑰/私鑰標(biāo)簽
#define kPublicKeyTag "com.logic.EncryptDemo.publickey"
#define kPrivateKeyTag "com.logic.EncryptDemo.privatekey"
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
static const uint8_t privateKeyIdentifier[] = kPrivateKeyTag;
@interface KRSACryptor() {
SecKeyRef publicKeyRef; // 公鑰引用
SecKeyRef privateKeyRef; // 私鑰引用
}
@property (nonatomic, retain) NSData *publicTag; // 公鑰標(biāo)簽
@property (nonatomic, retain) NSData *privateTag; // 私鑰標(biāo)簽
@end
@implementation KRSACryptor
+ (instancetype)shared {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
// 查詢密鑰的標(biāo)簽
_privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
_publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
}
return self;
}
#pragma mark - 加密 & 解密數(shù)據(jù)
- (NSData *)encryptData:(NSData *)plainData {
OSStatus sanityCheck = noErr;
size_t cipherBufferSize = 0;
size_t keyBufferSize = 0;
NSAssert(plainData != nil, @"明文數(shù)據(jù)為空");
NSAssert(publicKeyRef != nil, @"公鑰為空");
NSData *cipher = nil;
uint8_t *cipherBuffer = NULL;
// 計(jì)算緩沖區(qū)大小
cipherBufferSize = SecKeyGetBlockSize(publicKeyRef);
keyBufferSize = [plainData length];
if (kTypeOfWrapPadding == kSecPaddingNone) {
NSAssert(keyBufferSize <= cipherBufferSize, @"加密內(nèi)容太大");
} else {
NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密內(nèi)容太大");
}
// 分配緩沖區(qū)
cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
memset((void *)cipherBuffer, 0x0, cipherBufferSize);
// 使用公鑰加密
sanityCheck = SecKeyEncrypt(publicKeyRef,
kTypeOfWrapPadding,
(const uint8_t *)[plainData bytes],
keyBufferSize,
cipherBuffer,
&cipherBufferSize
);
NSAssert(sanityCheck == noErr, @"加密錯誤,OSStatus == %d", sanityCheck);
// 生成密文數(shù)據(jù)
cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize];
if (cipherBuffer) free(cipherBuffer);
return cipher;
}
- (NSData *)decryptData:(NSData *)cipherData {
OSStatus sanityCheck = noErr;
size_t cipherBufferSize = 0;
size_t keyBufferSize = 0;
NSData *key = nil;
uint8_t *keyBuffer = NULL;
SecKeyRef privateKey = NULL;
privateKey = [self getPrivateKeyRef];
NSAssert(privateKey != NULL, @"私鑰不存在");
// 計(jì)算緩沖區(qū)大小
cipherBufferSize = SecKeyGetBlockSize(privateKey);
keyBufferSize = [cipherData length];
NSAssert(keyBufferSize <= cipherBufferSize, @"解密內(nèi)容太大");
// 分配緩沖區(qū)
keyBuffer = malloc(keyBufferSize * sizeof(uint8_t));
memset((void *)keyBuffer, 0x0, keyBufferSize);
// 使用私鑰解密
sanityCheck = SecKeyDecrypt(privateKey,
kTypeOfWrapPadding,
(const uint8_t *)[cipherData bytes],
cipherBufferSize,
keyBuffer,
&keyBufferSize
);
NSAssert1(sanityCheck == noErr, @"解密錯誤,OSStatus == %d", sanityCheck);
// 生成明文數(shù)據(jù)
key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)keyBufferSize];
if (keyBuffer) free(keyBuffer);
return key;
}
#pragma mark - 密鑰處理
/**
* 生成密鑰對
*/
- (void)generateKeyPair:(NSUInteger)keySize {
OSStatus sanityCheck = noErr;
publicKeyRef = NULL;
privateKeyRef = NULL;
NSAssert1((keySize == 512 || keySize == 1024 || keySize == 2048), @"密鑰尺寸無效 %tu", keySize);
// 刪除當(dāng)前密鑰對
[self deleteAsymmetricKeys];
// 容器字典
NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
// 設(shè)置密鑰對的頂級字典
[keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(__bridge id)kSecAttrKeySizeInBits];
// 設(shè)置私鑰字典
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
[privateKeyAttr setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
// 設(shè)置公鑰字典
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
[publicKeyAttr setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag];
// 設(shè)置頂級字典屬性
[keyPairAttr setObject:privateKeyAttr forKey:(__bridge id)kSecPrivateKeyAttrs];
[keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];
// SecKeyGeneratePair 返回密鑰對引用
sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);
NSAssert((sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL), @"生成密鑰對失敗");
}
/**
* 加載公鑰
*/
- (void)loadPublicKey:(NSString *)publicKeyPath {
NSAssert(publicKeyPath.length != 0, @"公鑰路徑為空");
// 刪除當(dāng)前公鑰
if (publicKeyRef) CFRelease(publicKeyRef);
// 從一個 DER 表示的證書創(chuàng)建一個證書對象
NSData *certificateData = [NSData dataWithContentsOfFile:publicKeyPath];
SecCertificateRef certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
NSAssert(certificateRef != NULL, @"公鑰文件錯誤");
// 返回一個默認(rèn) X509 策略的公鑰對象,使用之后需要調(diào)用 CFRelease 釋放
SecPolicyRef policyRef = SecPolicyCreateBasicX509();
// 包含信任管理信息的結(jié)構(gòu)體
SecTrustRef trustRef;
// 基于證書和策略創(chuàng)建一個信任管理對象
OSStatus status = SecTrustCreateWithCertificates(certificateRef, policyRef, &trustRef);
NSAssert(status == errSecSuccess, @"創(chuàng)建信任管理對象失敗");
// 信任結(jié)果
SecTrustResultType trustResult;
// 評估指定證書和策略的信任管理是否有效
status = SecTrustEvaluate(trustRef, &trustResult);
NSAssert(status == errSecSuccess, @"信任評估失敗");
// 評估之后返回公鑰子證書
publicKeyRef = SecTrustCopyPublicKey(trustRef);
NSAssert(publicKeyRef != NULL, @"公鑰創(chuàng)建失敗");
if (certificateRef) CFRelease(certificateRef);
if (policyRef) CFRelease(policyRef);
if (trustRef) CFRelease(trustRef);
}
/**
* 加載私鑰
*/
- (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password {
NSAssert(privateKeyPath.length != 0, @"私鑰路徑為空");
// 刪除當(dāng)前私鑰
if (privateKeyRef) CFRelease(privateKeyRef);
NSData *PKCS12Data = [NSData dataWithContentsOfFile:privateKeyPath];
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
CFStringRef passwordRef = (__bridge CFStringRef)password;
// 從 PKCS #12 證書中提取標(biāo)示和證書
SecIdentityRef myIdentity;
SecTrustRef myTrust;
const void *keys[] = {kSecImportExportPassphrase};
const void *values[] = {passwordRef};
CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
// 返回 PKCS #12 格式數(shù)據(jù)中的標(biāo)示和證書
OSStatus status = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
if (status == noErr) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
myIdentity = (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
myTrust = (SecTrustRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
}
if (optionsDictionary) CFRelease(optionsDictionary);
NSAssert(status == noErr, @"提取身份和信任失敗");
SecTrustResultType trustResult;
// 評估指定證書和策略的信任管理是否有效
status = SecTrustEvaluate(myTrust, &trustResult);
NSAssert(status == errSecSuccess, @"信任評估失敗");
// 提取私鑰
status = SecIdentityCopyPrivateKey(myIdentity, &privateKeyRef);
NSAssert(status == errSecSuccess, @"私鑰創(chuàng)建失敗");
}
/**
* 刪除非對稱密鑰
*/
- (void)deleteAsymmetricKeys {
OSStatus sanityCheck = noErr;
NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];
// 設(shè)置公鑰查詢字典
[queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPublicKey setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
// 設(shè)置私鑰查詢字典
[queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
// 刪除私鑰
sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPrivateKey);
NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"刪除私鑰錯誤,OSStatus == %d", sanityCheck);
// 刪除公鑰
sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPublicKey);
NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"刪除公鑰錯誤,OSStatus == %d", sanityCheck);
if (publicKeyRef) CFRelease(publicKeyRef);
if (privateKeyRef) CFRelease(privateKeyRef);
}
/**
* 獲得私鑰引用
*/
- (SecKeyRef)getPrivateKeyRef {
OSStatus sanityCheck = noErr;
SecKeyRef privateKeyReference = NULL;
if (privateKeyRef == NULL) {
NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init];
// 設(shè)置私鑰查詢字典
[queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
// 獲得密鑰
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyReference);
if (sanityCheck != noErr) {
privateKeyReference = NULL;
}
} else {
privateKeyReference = privateKeyRef;
}
return privateKeyReference;
}
@end
4.2.2 測試驗(yàn)證
-
工程目錄如下:
image.png 測試代碼如下:
- (void) testRSAEncrpt {
//1.加載公鑰
[[KRSACryptor shared] loadPublicKey:[[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil]];
//2.加載私鑰
[[KRSACryptor shared] loadPrivateKey: [[NSBundle mainBundle] pathForResource:@"p.p12" ofType:nil] password:@"123456"];
}
static void my_encrypt(){
NSData * result = [[KRSACryptor shared] encryptData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]];
//base64編碼
NSString * base64 = [result base64EncodedStringWithOptions:0];
NSLog(@"加密之后:%@\n",base64);
//解密
NSData * dcStr = [[KRSACryptor shared] decryptData:result];
NSLog(@"%@",[[NSString alloc] initWithData:dcStr encoding:NSUTF8StringEncoding]);
}
-
打印結(jié)果如下:
image.png








































