RSA 概述
RSA是由Ron Rivest、Adi Shamir、Leonard Adleman三人于1977年提出,并以三人名字的首字母命名。
RSA的解密难度主要是基于大数分解。也就是说,给定的整数因数分解越困难,该RSA的破解难度越大。目前世界上尚未有已公布的有效破解RSA加密的方法。
RSA算法思想
一个优秀的公钥加密算法需要满足一下几个条件:
- 用户产生一对密钥(公钥,私钥)在计算上是容易的。
- 针对产生的密钥,加密明文的过程在计算上是容易的。
- 接收方使用其私钥对接收的密文以恢复明文在计算上是容易的。
- 在第三方已知公钥时,想要确定私钥在计算上是不可行的。
- 在第三方已知公钥与密文时,想要恢复明文在计算上不可行。
- {非必须} 加密和解密的顺序可以交换。
RSA作为自其诞生之日起就成为被广泛接收且被实现的通用公钥加密方法,其设计满足所有优秀的公钥加密算法的条件。
非对称加密算法产生公钥Kpublic=(n,e) 以及私钥 Kprivate=(n,d)。
RSA采用幂指数能够有效加大解密难度,即加密过程:
C
i
p
h
e
r
≡
M
e
(
m
o
d
n
)
Cipher \equiv M^e (modn)
Cipher≡Me(modn)
加密过程确定后,解密的过程也就应该是
P
l
a
i
n
t
e
x
t
=
M
≡
C
d
(
m
o
d
n
)
Plaintext=M \equiv C^d (mod n)
Plaintext=M≡Cd(modn)
需要上述条件成立,也就是要求
M
≡
(
M
d
)
e
≡
M
e
d
(
m
o
d
n
)
M \equiv(M^d)^e \equiv M^{ed} (mod n)
M≡(Md)e≡Med(modn)
对上式变形
M
≡
M
e
d
≡
M
∗
M
e
d
−
1
(
m
o
d
n
)
M \equiv M^{ed} \equiv M*M^{ed-1} (mod n)
M≡Med≡M∗Med−1(modn)
即
M
e
d
−
1
≡
1
(
m
o
d
n
)
M^{ed -1} \equiv 1(mod n)
Med−1≡1(modn)
看到上式我们很容易想到费马小定理和 欧拉定理:
前者:
a
p
−
1
≡
1
m
o
d
p
a^{p-1} \equiv 1 mod p
ap−1≡1modp
若我们采用费马小定理,令ed=np-1,很容易发现问题,当e,n公开时,d很容易求得。
所以令n为两个大素数因子的乘积p* q,采用欧拉定理(费马小定理其实是欧拉定理的一个特殊情况)欧拉定理表明,若n,a为正整数,且n,a互质,则
a
Φ
(
n
)
≡
1
(
m
o
d
n
)
a^{ \Phi(n) } \equiv 1 (mod n)
aΦ(n)≡1(modn)
已知n=p*q,且p,q为大素数,欧拉方程满足:
Φ
(
n
)
=
Φ
(
p
q
)
=
Φ
(
p
)
∗
Φ
(
q
)
=
(
p
−
1
)
(
q
−
1
)
\Phi(n)=\Phi(pq)=\Phi(p)*\Phi(q)=(p-1)(q-1)
Φ(n)=Φ(pq)=Φ(p)∗Φ(q)=(p−1)(q−1)
那么我们仅仅需要找到适合的公钥e与私钥d使得下式成立:
e
d
≡
1
[
m
o
d
Φ
(
n
)
]
ed \equiv 1 [mod \Phi(n) ]
ed≡1[modΦ(n)]
PS: 细心的朋友可能已经看到,前文在推论RSA算法的时候所依赖的欧拉定理与费马定义都要求了消息M与n互素。n并非是素数,而是两个素数的乘积,那么是否需要M与p、q两两互素呢?学习过的同学应该知道RSA对明文M的要求只有M<n。那么为什么不要求M小于min(p,q)呢?我们来继续讨论。
实际上,RSA算法成立的主要条件是:
M
≡
M
e
d
≡
M
k
ϕ
(
n
)
+
1
(
m
o
d
n
)
M \equiv M^{ed} \equiv M^{k \phi(n)+1}(mod n)
M≡Med≡Mkϕ(n)+1(modn)
那我们分两种情况讨论:
- 当密文M与n互素时,上文已证。
- 当密文M与n不互素时,M=xp(或者xq,这里仍取一个字母代替),
∵M<n ➡1≤x<q。
又p,q互素,由欧拉定理可得
( x p ) ϕ ( q ) ≡ 1 m o d q (xp)^{ \phi(q) } \equiv 1 mod q (xp)ϕ(q)≡1modq
所以进而推到:
[ ( x p ) ϕ ( q ) ] ϕ ( p ) ≡ ( x p ) ϕ ( q ) ∗ ϕ ( q ) ≡ ( x p ) ϕ ( n ) ≡ 1 m o d q [(xp)^{ \phi(q) }]^{ \phi(p) }\equiv (xp)^{ \phi(q)*\phi(q) } \equiv (xp)^{ \phi(n) } \equiv 1 mod q [(xp)ϕ(q)]ϕ(p)≡(xp)ϕ(q)∗ϕ(q)≡(xp)ϕ(n)≡1modq
令
( x p ) ϕ ( n ) = t q + 1 , 其 中 t 为 正 整 数 。 (xp)^{ \phi(n)} = tq+1 ,其中t为正整数。 (xp)ϕ(n)=tq+1,其中t为正整数。
则
M k ϕ ( n ) + 1 = ( x p ) k ϕ ( n ) + 1 = x p ∗ ( t q + 1 ) k M^{k \phi(n)+1}=(xp)^{k \phi(n)+1} =xp*(tq+1)^k Mkϕ(n)+1=(xp)kϕ(n)+1=xp∗(tq+1)k
展开k次幂的多项式后,在mod算法中含pq项皆可消除,明显可得
M k ϕ ( n ) + 1 m o d n = x p = M M^{k \phi(n)+1} modn = xp=M Mkϕ(n)+1modn=xp=M
总结: RSA通信前需要确定两个素数p和q。选择e,并求得e关于模Φ(n)的逆元d。正向计算过程简单,其破解难度在于大数分解。对密文M的要求是:M<n,所以RSA也是一种分组密码。
RSA具体流程
密钥产生
在应用公钥密码进行通信前,通信各方都必须产生一对密钥,即需做一下工作:
- 确定两个大素数p和q
- 选择e,并计算d=e-1 mod Φ(n)
挑选素数的过程如下:
(1)利用随机数发生器等方法选择一个奇整数x。
(2)随机选择一个整数a<x。
(3)执行Miller-Rabin等素性检测。若x未通过测试,则拒绝x,回到步骤(1)。
(4)若x通过足够多测素性检测则选择x;否则重复步骤(2)(3)。
假设A用户采用RSA加密算法对B用户发送的信息进行加密。
- A需要在沟通前选择两个较大素数p、q(p≠q,提高大数分解难度,避免开平方攻击)。
- 计算n=pq,Φ(n)=(p-1)(q-1)。
- 挑选出一个数e,使得e与Φ(n)互素。
- 计算e在模Φ(n)的逆元d。
- 公钥为PU={e,n}
- 私钥为PR={d,n}
加密过程
在RSA算法思想中提到,RSA的推导过程中用到了欧拉定理,其中要求了底数M与n互素,则明文的大小是有限制的。对于过长的明文需要分组。所以,RSA体制是一种分组密码。(n一般是1024位二进制数或309位十进制数,即n<2^1024)
- | - |
---|---|
明文 | M<n |
密文 | C=Me(mod n) |
解密过程
- | - |
---|---|
密文 | C |
明文 | M=Cd(mod n) |
RSA的简单实现
/*RSA算法
*1. A需要在沟通前选择两个较大素数p、q(p≠q,提高大数分解难度,避免开平方攻击)。
*2. 计算n=p*q,Φ(n)=(p-1)*(q-1)。
*3. 挑选出一个数e,使得e与Φ(n)互素。
*4. 计算e在模Φ(n)的逆元d。
*5. 公钥为PU={e,n}
*6. 私钥为PR={d,n}
*/
public class RSA {
static BigInteger e,d,n;
static BigInteger one = new BigInteger ("1");
//Initialization key
public static void initKey() {
//First Step is Generate two prime numbers of type BigInteger
BigInteger q = new BigInteger(1024,500,new Random());
BigInteger p = new BigInteger(1024,500,new Random());
//Second step is to caculate n=p*q and Φ(n)=(p-1)(q-1)
n = p.multiply(q);
BigInteger phi=p.subtract(one).multiply(q.subtract(one));
//The third step is choose a number e which is relatively prime with Φ(n)
e=new BigInteger(512, 500, new Random());
//caculate the multiplicative inverse element of e modulo phi by using Ex-Eculid algorithm
d=e.modInverse(phi);
}
//Encryption
public static BigInteger encrypt(BigInteger m) {
return m.modPow(e, n);
}
//Decryption
public static BigInteger decrypt(BigInteger c) {
return c.modPow(d, n);
}
public static void main(String args[]){
initKey();
BigInteger m = new BigInteger("123456789");
BigInteger c=encrypt(m);
System.out.println("解密后 : "+ decrypt(c));
}
}
JDK1.4以上其实还提供了RSA加解密工具类:KeyPairGenerator和Cipher。并可以提供其他的公钥加密算法。使用十分方便简单,具体RSA实现如下:
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
public class JDKRSA {
// JDK内置的RSA加解密工具类
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
// 生成RSA密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//Encryption
Cipher ce=Cipher.getInstance("RSA");
ce.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate());
byte m[]="secret".getBytes();
byte c[]=ce.doFinal(m);
//Decryption
Cipher cd = Cipher.getInstance("RSA");
cd.init(Cipher.DECRYPT_MODE, keyPair.getPublic());
byte m1[]=cd.doFinal(c);
System.out.println(new String(m1));
}
}
RSA困难问题
上文已经叙述了,RSA的思想与实现。那我们又来讨论为什么RSA是安全的,即为什么RSA会被归约到大素数分解困难问题上。
消息加密后能够正确解密的先决条件是, M < n M<n M<n。那么 n n n是一定需要公开的。而系统公钥 e e e也是需要公开的。则攻击者可获得公钥键值对 ( e , n ) (e,n) (e,n)。
目标需求1: 需要求得系统私钥
d
d
d
我们已知
e
d
m
o
d
=
1
m
o
d
ϕ
(
n
)
ed\,mod = 1\,mod\, \phi(n)
edmod=1modϕ(n)
则目标需求1可以 写作:
目标需求1’: 已知
e
,
ϕ
(
n
)
e,\phi(n)
e,ϕ(n)未知,求
d
d
d.
即求得
ϕ
(
n
)
\phi(n)
ϕ(n)后,可以获知系统私钥d。
所以该问题可以变为:
目标需求2: 已知 n n n,求 ϕ ( n ) \phi(n) ϕ(n)
由于n的比特位设置长度,直接求的计算量太大,可行性不高。
则回想
n
n
n的设置的特殊性,
n
n
n的设置实际上是两个素数
p
,
q
p,q
p,q的乘积,且
ϕ
(
n
)
=
ϕ
(
p
)
ϕ
(
q
)
=
(
p
−
1
)
)
(
q
−
1
)
\phi(n)=\phi(p)\phi(q)=(p-1))(q-1)
ϕ(n)=ϕ(p)ϕ(q)=(p−1))(q−1).
所以,RSA的困难问题最终被归约到 已知 n n n,求素因子 p , q p,q p,q.,即素数分解问题。