奇妙的安全旅行之RSA算法
hi,大家好,我是開發者FTD。今天我們開始介紹非對稱加密算法。非對稱加密算法區別于對稱加密算法的主要特點是,非對稱加密算法有兩個密鑰:公鑰 (public key) 和私鑰 (private key)。公鑰和私鑰是一對密鑰,如果用公鑰對數據加密,那么只能用對應的私鑰解密;相同的,如果用私鑰對數據加密,只能用對應的公鑰進行解密。因為加密和解密用的是不同的密鑰,所以將這種加密算法稱為非對稱加密。
非對稱加密算法的安全性好,由于有兩個密鑰,所以它不需要交換比較重要的私鑰,只需要交換對外公開的公鑰即可,它消除了用戶最終交換密鑰的需要。不過非對稱加密算法的加解密速度要遠遠慢于對稱加密,在某些極端情況下,甚至能比對稱加密慢上1000倍。
RSA 算法簡介
RSA 算法是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。RSA 算法既能用于加密,也能用于做數字簽名。
RSA 算法非常可靠,密鑰越長,它就越難破解。根據已經披露的文獻,目前被破解的最長RSA密鑰是768個二進制位。也就是說,長度超過768位的密鑰,還無法破解(至少沒人公開宣布)。因此可以認為,1024位的RSA密鑰基本安全,2048位的密鑰極其安全。
算法原理:
RSA 是目前最有影響力的公鑰加密算法,該算法基于一個十分簡單的數論事實:將兩個大素數相乘十分容易,但想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密密鑰,即公鑰,而兩個大素數組合成私鑰。公鑰是可發布的供任何人使用,私鑰則為自己所有,供解密之用。
RSA的公鑰、私鑰的組成,以及加密、解密的公式可見于下表:
| 私鑰 KR | d:e^-1 (mod(p-1)(q-1)) n: p 和 q 的乘積 |
| 加密 | C ≡ m^e mod n |
| 解密 | m ≡ c^d mod n |
大家看了這些公式估計有些懵,沒關系,我們一個一個解釋一下。首先我們先復習一下數學中的一些基礎概念:
什么是“素數”?
素數也稱為“質數”,是指除了能表示為它自己和1的乘積以外,不能表示為任何其它兩個整數的乘積的數字。
例如,15=3*5,所以15不是素數;又如,12=6*2=4*3,所以12也不是素數。又例如,13除了等于13*1以外,不能表示為其它任何兩個整數的乘積,所以13是一個素數。
什么是“互質數”(或“互素數”)?
小學數學教材對互質數是這樣定義的:“公約數只有1的兩個數,叫做互質數。” 這里所說的“兩個數”是指自然數。
判別方法主要有以下幾種(不限于此):
- 兩個質數一定是互質數。例如,2與7、13與19。
- 一個質數如果不能整除另一個合數,這兩個數為互質數。例如,3與10、5與 26。
- 1不是質數也不是合數,它和任何一個自然數在一起都是互質數。如1和9908。
- 相鄰的兩個自然數是互質數。如 15與 16。
- 相鄰的兩個奇數是互質數。如 49與 51。
- 大數是質數的兩個數是互質數。如97與88。
- 小數是質數,大數不是小數的倍數的兩個數是互質數。如 7和 16。
- 兩個數都是合數(二數差又較大),小數所有的質因數,都不是大數的約數,這兩個數是互質數。如357與715,357=3×7×17,而3、7和17都不是715的約數,這兩個數為互質數。等等。
什么是模指數運算?
我們先說下指數運算,指數是位于一個未知數的右上方,表示這個未知數相乘幾次,例如:2 ^ 4 = 16;
模運算是整數運算,有一個整數m,以n為模做模運算,即m mod n。讓m去被n整除,只取所得的余數作為結果,就叫做模運算。例如,10 mod 3 = 1;26 mod 6 = 2;28 mod 2 = 0 等。
而模指數運算就是先做指數運算,取其結果后再做模運算。如
53mod7=125mod7=65 ^ 3 \quad mod \quad 7 \quad = \quad 125 \quad mod \quad 7 \quad = \quad 6 53mod7=125mod7=6
好,下面我們開始正式介紹RSA加密算法。
RSA 算法描述:
選擇一對不同的、足夠大的素數p,q。
計算 n = pq。
計算 f(n) = (p-1)(q-1) ,同時對p, q嚴加保密,不能讓其他任何人知道。
找一個與 f(n) 互質的數 e,且 1<e<f(n) 。
計算 d,使得 de ≡ 1 mod f(n)。這個公式也可以表達為d ≡ e^-1 mod f(n)
這里要解釋一下,≡ 是數論中表示同余的符號。公式中,≡ 符號的左邊必須和符號右邊同余,也就是兩邊模運算結果相同。顯而易見,不管f(n)取什么值,符號右邊1 mod f(n)的結果都等于1;符號的左邊d與e的乘積做模運算后的結果也必須等于1。這就需要計算出d的值,讓這個同余等式能夠成立。
公鑰 KU=(e,n),私鑰 KR=(d,n)。
加密時,先將明文變換成0至n-1的一個整數M。若明文較長,可先分割成適當的組,然后再進行交換。設密文為C,則加密過程為:
C≡MemodnC ≡ M^e \quad mod\quad n C≡Memodn
? 解密過程為:
M≡CdmondnM ≡ C^d\quad mond \quad n M≡Cdmondn
RSA 算法舉例:
在這里,我們無法對RSA算法的正確性作嚴格的數學證明,但我們可以通過一個簡單的例子來理解RSA算法的工作原理。為了便于計算。在以下實例中只選取小數值的素數p,q,以及e,假設用戶A需要將明文“key”通過RSA加密后傳遞給用戶B,過程如下:
(1)設計公私密鑰(e,n)和(d,n)。
令p=3,q=11,得出n=p×q=3×11=33;f(n)=(p-1)(q-1)=2×10=20;取e=3,(3與20互質)則e×d≡1 mod f(n),即 3×d ≡ 1 mod 20。
d怎樣取值呢?可以用試算的辦法來尋找。試算結果見下表:
| 1 | 3 | 3 |
| 2 | 6 | 6 |
| 3 | 9 | 9 |
| 4 | 12 | 12 |
| 5 | 15 | 15 |
| 6 | 18 | 18 |
| 7 | 21 | 1 |
| 8 | 24 | 3 |
| 9 | 27 | 6 |
通過試算我們找到,當d=7時,e×d≡1 mod f(n)同余等式成立。因此,可令d=7。從而我們可以設計出一對公私密鑰,加密密鑰(公鑰)為:KU =(e,n)=(3,33),解密密鑰(私鑰)為:KR =(d,n)=(7,33)。
(2)英文數字化。
將明文信息數字化,并將每塊兩個數字分組。假定明文英文字母編碼表為按字母順序排列數值,即:
| 碼值 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 |
| 字母 | n | o | p | q | r | s | t | u | v | w | x | y | z |
| 碼值 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
則得到分組后的key的明文信息為:11,05,25。
(3)明文加密
用戶加密密鑰(3,33) 將數字化明文分組信息加密成密文。根據公式
C≡Me(modn)C ≡ M^e \quad (mod\quad n) C≡Me(modn)
得:
113mod33=1111^3 \quad mod \quad 33 = 11 113mod33=11
53mod33=265^3 \quad mod \quad 33 = 26 53mod33=26
253mod33=1625^3 \quad mod \quad 33 = 16 253mod33=16
因此,得到相應的密文信息為:11,26,16。
(4)密文解密。
用戶B收到密文,若將其解密,只需要根據以下公式計算
M≡CdmondnM ≡ C^d\quad mond \quad n M≡Cdmondn
,即:
117mod33=1111^7 \quad mod \quad 33 = 11 117mod33=11
267mod33=0526^7 \quad mod \quad 33 = 05 267mod33=05
167mod33=2516^7 \quad mod \quad 33 = 25 167mod33=25
用戶B得到明文信息為:11,05,25。根據上面的編碼表將其轉換為英文,我們又得到了恢復后的原文“key”。
通過實例,我們可以很清楚的看清楚RSA算法的一個原理。
當然,實際在加密過程中要比這復雜得多,由于RSA算法的公鑰私鑰的長度(模長度)要到1024位甚至2048位才能保證安全,因此,p、q、e的選取、公鑰私鑰的生成,加密解密模指數運算都有一定的計算程序,需要仰仗計算機高速完成。
RSA 算法工作流程
A 要把信息發給 B 為例,確定角色:A 為加密者,B 為解密者。
首先由 B 隨機確定一個 KEY,稱之為私鑰,將這個 KEY 始終保存在機器 B 中而不發出來;
然后,由這個 KEY 計算出另一個 KEY,稱之為公鑰。這個公鑰的特性是幾乎不可能通過它自身計算出生成它的私鑰。
接下來通過網絡把這個公鑰傳給 A,A 收到公鑰后,利用公鑰對信息加密,并把密文通過網絡發送到 B。
最后 B 利用已知的私鑰,就能對密文進行解密了。
以上就是 RSA 算法的工作流程。
RSA 算法加密速度
由于進行的都是大數計算,使得 RSA 最快的情況也比 DES 慢上好幾倍,無論是軟件還是硬件實現。速度一直是 RSA 的缺陷。一般來說只用于少量數據加密。RSA 的速度是對應同樣安全級別的對稱密碼算法的1/1000左右。
比起 DES 和其它對稱算法來說,RSA 要慢得多。實際上一般使用一種對稱算法來加密信息,然后用 RSA 來加密比較短的公鑰,然后將用 RSA 加密的公鑰和用對稱算法加密的消息發送給接收方。
這樣一來對隨機數的要求就更高了,尤其對產生對稱密碼的要求非常高,否則的話可以越過 RSA 來直接攻擊對稱密碼。
公鑰傳遞安全
和其它加密過程一樣,對 RSA 來說分配公鑰的過程是非常重要的。分配公鑰的過程必須能夠抵擋中間人攻擊。
假設 A 交給 B 一個公鑰,并使 B 相信這是A 的公鑰,并且 C 可以截下 A 和 B 之間的信息傳遞,那么 C 可以將自己的公鑰傳給 B,B 以為這是 A 的公鑰。C 可以將所有 B 傳遞給 A 的消息截下來,將這個消息用自己的密鑰解密,讀這個消息,然后將這個消息再用 A 的公鑰加密后傳給 A。理論上 A 和 B 都不會發現 C 在偷聽它們的消息,今天人們一般用數字認證來防止這樣的攻擊。
RSA算法優缺點
優點:
缺點:
RSA 算法實現
RSA密鑰生成:
/*** 生成RSA 公私鑰,并存儲到字符串數組中** @return* @throws NoSuchAlgorithmException*/ public static String[] genRSAKeys2Str() throws NoSuchAlgorithmException {// 生成密匙對KeyPair kp = generateKeyPairs();//得到公鑰Key publicKey = kp.getPublic();// 得到私鑰Key privateKey = kp.getPrivate();// 得到公私鑰字節數組byte[] publicKeyBytes = publicKey.getEncoded();byte[] privateKeyBytes = privateKey.getEncoded();// 得到base64編碼后的公私鑰字符串String publicKeyStr = Base64.encodeBase64String(publicKeyBytes);String privateKeyStr = Base64.encodeBase64String(privateKeyBytes);String[] rsaKeys = new String[2];rsaKeys[0] = publicKeyStr;rsaKeys[1] = privateKeyStr;return rsaKeys; }/*** 生成密匙對 KeyPairs** @return KeyPair keyPair* @throws NoSuchAlgorithmException*/ private static KeyPair generateKeyPairs() throws NoSuchAlgorithmException {// RSA算法要求有一個可信任的隨機數源SecureRandom sr = new SecureRandom();// 為RSA算法創建一個KeyPairGenerator對象KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);// 利用上面的隨機數據源初始化這個KeyPairGenerator對象kpg.initialize(RSA_KEY_SIZE, sr);return kpg.generateKeyPair(); }公鑰加密:
/*** RSA 通過【公鑰】加密** @param pubKey* @param data* @return* @throws Exception*/ public static byte[] encryptByPubKey(byte[] pubKey, byte[] data) throws Exception {// 構建keyKey rsaPubKey = buildPubKey(pubKey);// 加密return encrypt(rsaPubKey, data); }// 生成KeySpec,注意公私鑰使用的生成類不同 private static Key buildPubKey(byte[] pubKey) throws Exception {// 創建KeySpecX509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKey);KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);return keyFactory.generatePublic(keySpec); }// 加密方法 private static byte[] encrypt(Key key, byte[] data) throws Exception {Cipher cipher = Cipher.getInstance(RSA_CIPTHER);cipher.init(Cipher.ENCRYPT_MODE, key);byte[] encryptData = cipher.doFinal(data);return encryptData; }私鑰解密:
/*** RSA 使用【私鑰】解密** @param priKey 密鑰* @param encryptData 密文數據* @return 明文字節數組* @throws Exception 解密過程中的異常信息*/ public static byte[] decryptByPriKey(byte[] priKey, byte[] encryptData)throws Exception {Key rsaPriKey = buildPriKey(priKey);// 返回解密數據return decrypt(rsaPriKey, encryptData); }// 生成KeySpec,注意公私鑰使用的生成類不同 private static Key buildPriKey(byte[] priKey) throws Exception {// 創建KeySpecPKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(priKey);KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);return keyFactory.generatePrivate(keySpec); }// 解密 private static byte[] decrypt(Key key, byte[] encryptData) throws Exception {Cipher cipher = Cipher.getInstance(RSA_CIPTHER);cipher.init(Cipher.DECRYPT_MODE, key);byte[] decryptData = cipher.doFinal(encryptData);// 返回解密數據return decryptData; }RSA 算法應用場景
(1) 信息加密
收信者是唯一能夠解開加密信息的人,因此收信者手里的必須是私鑰。發信者手里的是公鑰,其它人知道公鑰沒有關系,因為其它人發來的信息對收信者沒有意義。
(2) 登錄認證
客戶端需要將認證標識傳送給服務器,此認證標識 (可能是一個隨機數) 其它客戶端可以知道,因此需要用私鑰加密,客戶端保存的是私鑰。服務器端保存的是公鑰,其它服務器知道公鑰沒有關系,因為客戶端不需要登錄其它服務器。
(3) 數字簽名
數字簽名是為了表明信息沒有受到偽造,確實是信息擁有者發出來的,附在信息原文的后面。就像手寫的簽名一樣,具有不可抵賴性和簡潔性。
簡潔性:對信息原文做哈希運算,得到消息摘要,信息越短加密的耗時越少。
不可抵賴性:信息擁有者要保證簽名的唯一性,必須是唯一能夠加密消息摘要的人,因此必須用私鑰加密 (就像字跡他人無法學會一樣),得到簽名。如果用公鑰,那每個人都可以偽造簽名了。
(4) 數字證書
問題起源:對1和3,發信者怎么知道從網上獲取的公鑰就是真的?沒有遭受中間人攻擊?
這樣就需要第三方機構來保證公鑰的合法性,這個第三方機構就是 CA (Certificate Authority),證書中心。
CA 用自己的私鑰對信息原文所有者發布的公鑰和相關信息進行加密,得出的內容就是數字證書。
信息原文的所有者以后發布信息時,除了帶上自己的簽名,還帶上數字證書,就可以保證信息不被篡改了。信息的接收者先用 CA給的公鑰解出信息所有者的公鑰,這樣可以保證信息所有者的公鑰是真正的公鑰,然后就能通過該公鑰證明數字簽名是否真實了。
總結
RSA 算法是目前使用最為廣泛的非對稱加密算法,你可以在各種對安全要求比較高的場景中看到它的身影,所以掌握RSA 算法是我們工作中比不缺的一項技能。
創作不易,如果大家喜歡本文,歡迎點贊,轉發,你的關注是我們繼續前進的動力_
參考:
1,RSA算法理解
2,加解密篇 - 非對稱加密算法 (RSA、DSA、ECC、DH)
關于作者
-
GitHub:https://github.com/ForTheDevelopers
-
掘金:https://juejin.cn/user/1204720472953022/posts
-
CSDN:https://blog.csdn.net/ForTheDevelopers
-
segmentfault:https://segmentfault.com/u/for_the_developers
聯系作者
-
微信號:ForTheDeveloper
-
公眾號:ForTheDevelopers
總結
以上是生活随笔為你收集整理的奇妙的安全旅行之RSA算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WinSock I/O 模型 -- OV
- 下一篇: 风机桨叶故障诊断(七) 滑动窗与非极大值