RSA加密算法是最常用的非對稱加密算法,它既能用于加密,也能用于數(shù)字簽名。RSA以它的三個發(fā)明者Ron Rivest, Adi Shamir, Leonard Adleman的名字首字母命名,這個算法經(jīng)受住了多年深入的密碼分析,雖然密碼分析者既不能證明也不能否定RSA的安全性,但這恰恰說明該算法有一定的可信性,目前它已經(jīng)成為最流行的公開密鑰算法。
RSA的安全基于大數(shù)分解的難度。其公鑰和私鑰是一對大素數(shù)(100到200位十進制數(shù)或更大)的函數(shù)。從一個公鑰和密文恢復(fù)出明文的難度,等價于分解兩個大素數(shù)之積(這是公認的數(shù)學(xué)難題)。

讓我們先復(fù)習(xí)一下數(shù)學(xué)上的幾個基本概念,它們在后面的介紹中要用到:
一、 什么是“素數(shù)”? 素數(shù)是這樣的整數(shù),它除了能表示為它自己和1的乘積以外,不能表示為任何其它兩個整數(shù)的乘積。例如,15=3*5,所以15不是素數(shù);又如,12=6*2=4*3,所以12也不是素數(shù)。另一方面,13除了等于13*1以外,不能表示為其它任何兩個整數(shù)的乘積,所以13是一個素數(shù)。素數(shù)也稱為“質(zhì)數(shù)”。
二、什么是“互質(zhì)數(shù)”(或“互素數(shù)”)? 小學(xué)數(shù)學(xué)教材對互質(zhì)數(shù)是這樣定義的:“公約數(shù)只有1的兩個數(shù),叫做互質(zhì)數(shù)?!边@里所說的“兩個數(shù)”是指自然數(shù)。
判別方法主要有以下幾種(不限于此):
(1)兩個質(zhì)數(shù)一定是互質(zhì)數(shù)。例如,2與7、13與19。
(2)一個質(zhì)數(shù)如果不能整除另一個合數(shù),這兩個數(shù)為互質(zhì)數(shù)。例如,3與10、5與 26。
(3)1不是質(zhì)數(shù)也不是合數(shù),它和任何一個自然數(shù)在一起都是互質(zhì)數(shù)。如1和9908。
(4)相鄰的兩個自然數(shù)是互質(zhì)數(shù)。如 15與 16。
(5)相鄰的兩個奇數(shù)是互質(zhì)數(shù)。如 49與 51。
(6)大數(shù)是質(zhì)數(shù)的兩個數(shù)是互質(zhì)數(shù)。如97與88。
(7)小數(shù)是質(zhì)數(shù),大數(shù)不是小數(shù)的倍數(shù)的兩個數(shù)是互質(zhì)數(shù)。如 7和 16。
(8)兩個數(shù)都是合數(shù)(二數(shù)差又較大),小數(shù)所有的質(zhì)因數(shù),都不是大數(shù)的約數(shù),這兩個數(shù)是互質(zhì)數(shù)。如357與715,357=3×7×17,而3、7和17都不是715的約數(shù),這兩個數(shù)為互質(zhì)數(shù)。等等。
三、什么是模指數(shù)運算?
指數(shù)運算誰都懂,不必說了,先說說模運算。模運算是整數(shù)運算,有一個整數(shù)m,以n為模做模運算,即m mod n。怎樣做呢?讓m去被n整除,只取所得的余數(shù)作為結(jié)果,就叫做模運算。例如,10 mod 3=1;26 mod 6=2;28 mod 2 =0等等。
模指數(shù)運算就是先做指數(shù)運算,取其結(jié)果再做模運算。如

好,現(xiàn)在開始正式講解RSA加密算法。
算法描述:
(1)選擇一對不同的、足夠大的素數(shù)p,q。
(2)計算n=pq。
(3)計算f(n)=(p-1)(q-1),同時對p, q嚴加保密,不讓任何人知道。
(4)找一個與f(n)互質(zhì)的數(shù)e,且1<e<f(n)。
(5)計算d,使得de≡1 mod f(n)。這個公式也可以表達為d ≡e-1 mod f(n)
這里要解釋一下,≡是數(shù)論中表示同余的符號。公式中,≡符號的左邊必須和符號右邊同余,也就是兩邊模運算結(jié)果相同。顯而易見,不管f(n)取什么值,符號右邊1 mod f(n)的結(jié)果都等于1;符號的左邊d與e的乘積做模運算后的結(jié)果也必須等于1。這就需要計算出d的值,讓這個同余等式能夠成立。
(6)公鑰KU=(e,n),私鑰KR=(d,n)。
(7)加密時,先將明文變換成0至n-1的一個整數(shù)M。若明文較長,可先分割成適當?shù)慕M,然后再進行交換。設(shè)密文為C,則加密過程為:


實例描述: 在這篇科普小文章里,不可能對RSA算法的正確性作嚴格的數(shù)學(xué)證明,但我們可以通過一個簡單的例子來理解RSA的工作原理。為了便于計算。在以下實例中只選取小數(shù)值的素數(shù)p,q,以及e,假設(shè)用戶A需要將明文“key”通過RSA加密后傳遞給用戶B,過程如下:
(1)設(shè)計公私密鑰(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互質(zhì))則e×d≡1 mod f(n),即3×d≡1 mod 20。
d怎樣取值呢?可以用試算的辦法來尋找。試算結(jié)果見下表:

通過試算我們找到,當d=7時,e×d≡1 mod f(n)同余等式成立。因此,可令d=7。從而我們可以設(shè)計出一對公私密鑰,加密密鑰(公鑰)為:KU =(e,n)=(3,33),解密密鑰(私鑰)為:KR =(d,n)=(7,33)。
(2)英文數(shù)字化。 將明文信息數(shù)字化,并將每塊兩個數(shù)字分組。假定明文英文字母編碼表為按字母順序排列數(shù)值,即:

則得到分組后的key的明文信息為:11,05,25。
(3)明文加密
用戶加密密鑰(3,33) 將數(shù)字化明文分組信息加密成密文。由C≡Me(mod n)得:

因此,得到相應(yīng)的密文信息為:11,31,16。
(4)密文解密。
用戶B收到密文,若將其解密,只需要計算

,即:

用戶B得到明文信息為:11,05,25。根據(jù)上面的編碼表將其轉(zhuǎn)換為英文,我們又得到了恢復(fù)后的原文“key”。
你看,它的原理就可以這么簡單地解釋!
當然,實際運用要比這復(fù)雜得多,由于RSA算法的公鑰私鑰的長度(模長度)要到1024位甚至2048位才能保證安全,因此,p、q、e的選取、公鑰私鑰的生成,加密解密模指數(shù)運算都有一定的計算程序,需要仰仗計算機高速完成。
最后簡單談?wù)凴SA的安全性
首先,我們來探討為什么RSA密碼難于破解?
在RSA密碼應(yīng)用中,公鑰KU是被公開的,即e和n的數(shù)值可以被第三方竊聽者得到。破解RSA密碼的問題就是從已知的e和n的數(shù)值(n等于pq),想法求出d的數(shù)值,這樣就可以得到私鑰來破解密文。從上文中的公式:d ≡e-1 (mod((p-1)(q-1)))或de≡1 (mod((p-1)(q-1))) 我們可以看出。密碼破解的實質(zhì)問題是:從Pq的數(shù)值,去求出(p-1)和(q-1)。換句話說,只要求出p和q的值,我們就能求出d的值而得到私鑰。
當p和q是一個大素數(shù)的時候,從它們的積pq去分解因子p和q,這是一個公認的數(shù)學(xué)難題。比如當pq大到1024位時,迄今為止還沒有人能夠利用任何計算工具去完成分解因子的任務(wù)。因此,RSA從提出到現(xiàn)在已近二十年,經(jīng)歷了各種攻擊的考驗,逐漸為人們接受,普遍認為是目前最優(yōu)秀的公鑰方案之一。
然而,雖然RSA的安全性依賴于大數(shù)的因子分解,但并沒有從理論上證明破譯RSA的難度與大數(shù)分解難度等價。即RSA的重大缺陷是無法從理論上把握它的保密性能如何。
此外,RSA的缺點還有:A)產(chǎn)生密鑰很麻煩,受到素數(shù)產(chǎn)生技術(shù)的限制,因而難以做到一次一密。B)分組長度太大,為保證安全性,n 至少也要 600 bits 以上,使運算代價很高,尤其是速度較慢,較對稱密碼算法慢幾個數(shù)量級;且隨著大數(shù)分解技術(shù)的發(fā)展,這個長度還在增加,不利于數(shù)據(jù)格式的標準化。因此,使用RSA只能加密少量數(shù)據(jù),大量的數(shù)據(jù)加密還要靠對稱密碼算法。
用公鑰加密時,私鑰可以解密。反之亦然,私鑰加密后的信息用公鑰可以解密。
Linux 下通過 OpenSSL 生成 RSA 公鑰和私鑰
需要提前在 Linux 上安裝 OpenSSL,默認生成在當前用戶家目錄下:
[root@VM_120_242_centos ~]# openssl
OpenSSL> genrsa -out app_private_key.pem 1024 # 生成私鑰
Generating RSA private key, 1024 bit long modulus
.++++++
........++++++
e is 65537 (0x10001)
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 生成公鑰
writing RSA key
OpenSSL> exit
對于 PHP 可以直接使用上面生成的原始私鑰。但是 Java 需要將私鑰轉(zhuǎn)換成 PKCS8 格式,然后將生成的 PKCS8 格式的私鑰去除頭尾、換行和空格,作為私鑰字符串填入代碼中:
OpenSSL> pkcs8 -topk8 -inform PEM -in app_private_key.pem -outform PEM -nocrypt -out app_private_key_pkcs8.pem # 私鑰轉(zhuǎn)成 PKCS8 格式
查看生成的文件:
[root@VM_120_242_centos ~]# ll
總用量 1064
-rw-r--r-- 1 root root 887 6月 14 11:25 app_private_key.pem
-rw-r--r-- 1 root root 272 6月 14 11:25 app_public_key.pem
查看公鑰:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3//sR2tXw0wrC2DySx8vNGlqt
3Y7ldU9+LBLI6e1KS5lfc5jlTGF7KBTSkCHBM3ouEHWqp1ZJ85iJe59aF5gIB2kl
Bd6h4wrbbHA2XE1sq21ykja/Gqx7/IRia3zQfxGv/qEkyGOx+XALVoOlZqDwh76o
2n1vP1D+tD3amHsK7QIDAQAB
-----END PUBLIC KEY-----
轉(zhuǎn)成 PKCS8 格式的公鑰字符串為:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3//sR2tXw0wrC2DySx8vNGlqt3Y7ldU9+LBLI6e1KS5lfc5jlTGF7KBTSkCHBM3ouEHWqp1ZJ85iJe59aF5gIB2klBd6h4wrbbHA2XE1sq21ykja/Gqx7/IRia3zQfxGv/qEkyGOx+XALVoOlZqDwh76o2n1vP1D+tD3amHsK7QIDAQAB
查看私鑰:
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC3//sR2tXw0wrC2DySx8vNGlqt3Y7ldU9+LBLI6e1KS5lfc5jl
TGF7KBTSkCHBM3ouEHWqp1ZJ85iJe59aF5gIB2klBd6h4wrbbHA2XE1sq21ykja/
Gqx7/IRia3zQfxGv/qEkyGOx+XALVoOlZqDwh76o2n1vP1D+tD3amHsK7QIDAQAB
AoGBAKH14bMitESqD4PYwODWmy7rrrvyFPEnJJTECLjvKB7IkrVxVDkp1XiJnGKH
2h5syHQ5qslPSGYJ1M/XkDnGINwaLVHVD3BoKKgKg1bZn7ao5pXT+herqxaVwWs6
ga63yVSIC8jcODxiuvxJnUMQRLaqoF6aUb/2VWc2T5MDmxLhAkEA3pwGpvXgLiWL
3h7QLYZLrLrbFRuRN4CYl4UYaAKokkAvZly04Glle8ycgOc2DzL4eiL4l/+x/gaq
deJU/cHLRQJBANOZY0mEoVkwhU4bScSdnfM6usQowYBEwHYYh/OTv1a3SqcCE1f+
qbAclCqeNiHajCcDmgYJ53LfIgyv0wCS54kCQAXaPkaHclRkQlAdqUV5IWYyJ25f
oiq+Y8SgCCs73qixrU1YpJy9yKA/meG9smsl4Oh9IOIGI+zUygh9YdSmEq0CQQC2
4G3IP2G3lNDRdZIm5NZ7PfnmyRabxk/UgVUWdk47IwTZHFkdhxKfC8QepUhBsAHL
QjifGXY4eJKUBm3FpDGJAkAFwUxYssiJjvrHwnHFbg0rFkvvY63OSmnRxiL4X6EY
yI9lblCsyfpl25l7l5zmJrAHn45zAiOoBrWqpM5edu7c
-----END RSA PRIVATE KEY-----