RSA是一种公钥密码算法,它的名字是由它的三位开发者,即Ron Rivest、Adi Shamir 和 Leonard Adleman 的姓氏的首字母组成的。RSA可以被用于公钥密码和数字签名。RSA是被研究得最广泛的公钥算法,从提出到现在已近三十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。1983年麻省理工学院在美国为RSA算法申请了专利。
RSA的安全性依赖于大数分解。换句话说,RSA的难度与大数分解难度等价,一旦发现了对大整数进行质因数分解的高效算法,RSA就能够被破译。这点将在下述对 N 的讨论着重体现。
参数 | 解释 | 公式 | 描述 |
---|---|---|---|
P、Q | 质数 | P*Q=N | 分解模数N后得到的值 |
N | 公共模数 | N=P*Q | 在RAS中进行模运算 |
E | 公钥指数 | gcd(φ,e)=1 | 1 |
D | 私钥指数 | gcd(φ,d)=1 | 1 |
φ | 欧拉公式 | φ=(P-1)*(Q-1) | / |
c | 密文 | c=me mod N | / |
d | 明文 | m=cd mod N | / |
由上面的参数表可知,RSA加密过程可以用下面这个公式表示
c ≡ m e mod N
也就是说,RSA的密文是对代表明文的数字 m 的 e 次方对 N 求余的结果
公钥
上述加密算法中出现的两个数—— E 和 N ,到底是什么数呢?由上面的加密算法可知,在已知明文的情况下,只要只要 E 和 N 这两个数,任何人都可以完成对加密的运算。所以说, E 和 N 是RSA加密的密钥,换句话说,E 和 N 的组合就是公钥,表示为公钥是(E,N)
公钥是可以任意公开的
同样由上面的参数表可知,RSA解密过程可以用下面这个公式表示
m≡cd mod N
也就是说,RSA的明文是对代表密文的数字 c 的 d 次方对 N 求余的结果
可以发现形式上跟加密算法高度对称
私钥
参考公钥的解释,私钥也就明了了。在已知密文的情况下,只要只要 D 和 N 这两个数,任何人都可以完成对解密的运算。所以说, D 和 N 是RSA解密的密钥,换句话说,D 和 N 的组合就是私钥,表示为私钥是(D,N)
由上述分析可知,加解密的过程需要三个参数 E、D、N,那么这三个参数该怎么生成呢?由于 E 和 N 是公钥,D 和 N 是私钥,求这三个数的过程就是生成密钥对。生成步骤如下:
1、求N
2、求φ
3、求E
4、求D
求N
首先准备两个很大的质数 P、Q。上述所说的算法安全性与大数分解有关,就体现在这了,我们反过来想,如果 p 和 q 选的很小,那对于 N 的求解将会变得非常简单,密码就容易被破译;但是物极必反,也不能选的太大,太大会使得计算时间大大延长。
准备好之后,N 由以下公式求得
P*Q=N
求φ
φ这个数在加密解密的过程中都不曾出现,它只出现在生成密钥对的过程中。由下列公式求得
φ=(P-1)*(Q-1)
求E
e 的求取基于上述的 φ 值。首先明确 e 的取值范围 1
gcd(φ,e)=1
(φ 与 e 的最大公约数为1,即两者互质)。之所以要加上1这个条件,是为了保证一定存在解密时需要使用的参数 D
至此,我们生成了密钥对中的公钥
求D
d 的求取基于上述的 e 值和 φ 值。首先明确 d 的取值范围 1
gcd(φ,d)=1
。d 的求解方式如下
e*d mod φ=1
密钥对生成
① 求N
首先随便找两个素数,比如3、11(这边自己实验就取小数展示)
p = 3
q = 11
得到 N
N = 3*11 = 33
② 求φ
φ = 2*10= 20
③ 求e
gcd(φ,e)=1
满足条件的有很多,3,7,11,13,17,19… 这边我们选择3来作为 e。
至此,e = 3 ,N = 33
,这就是公钥
④ 求d
e*d mod φ=1
容易得 d = 7
至此,d = 7 ,N = 33
,这就是私钥
加密
要加密的明文必须是小于 N 的数,也就是小于33的数,这边就随便取一个5吧
c=me mod N = 53 mod 33 = 26
解密
m=cd mod N = 267 mod 33 = 5
对于常规得CTF题来说,通常会给大家公钥指数 E 和公共模数 N,而这个模数 N 是非常大得数字。我们要做的是将它分解成两个指数 p,q,进而求得 φ,再根据公式求得私钥指数,最后将密文转换成明文。
如何将大整数 N 分解呢?
1、当 N 的长度较小时,采用爆破
2、当 N 满足因数p、q相差较小或相差很大时,可以采用离线工具yafu来分解
3、当 N 的位数过大时,且不满足上述条件,可以用在线网站 factordb。该网站类似于彩虹表,将已被分解的大数结果存储起来,只需要输入查询即可。
逆元是什么?为什么突然讨论逆元?
还记得上面求解私钥指数d的公式吗?
e*d mod φ=1
这个公式也可以写成
e*d ≡ 1(mod φ)
如果一个线性同余方程 ax ≡ 1(mod b)
,则 x 称为 a mod b
的逆元,记作a-1 。一个数有逆元的充分必要条件是gcd(a,b)=1,此时逆元唯一存在 。
此处为什么讨论逆元呢?其一,RSA中有逆元的概念;其二,中国剩余定理(CRT)可与 RSA 算法结合来进行加解密,CRT又逃不开逆元的概念,所以就说了。逆元也是数论中一个十分重要的概念,当我们要求 (a / b) mod p的值,且a很大,大到会溢出;或者说b很大,达到会爆精度。无法直接求得a/b的值时,我们就要用到乘法逆元。
所以上述求解私钥指数d,可以说 e 的逆元是 d mod φ
如果 p 是一个质数,而整数 a 不是 p 的倍数,则
a p−1 ≡ 1 (mod p)
可得
a * a p−2 ≡ 1 (mod p)
所以 a 的逆元即为 a p-2 (mod p)
之后利用快速幂求解
typedef long long ll;
ll mod = 1e9 + 7;
ll quick_pow(ll base,ll idx){
ll ans = 1;
while(idx){
if(idx & 1){
ans *= base;
ans %= mod;
}
base *= base;
base %= mod;
idx >>= 1;
}
return ans;
}
ll inv(ll a){
return quick_pow(a,mod-2);
}
逆元的含义决定了ax ≡ 1(mod b)
等价于ax = kb+1
void exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1, y = 0;
return;
}
exgcd(b, a % b, y, x);
y -= a / b * x;
}
网上介绍很多,具体可以参考中国剩余定理(孙子定理)
前面多次讨论过了,RSA的难点就在于对于大整数 N 的分解。有没有啥方法能简化这个过程呢?
有,就是上述的中国剩余定理。由中国剩余定理可知,设 p 和 q 是互相独立的大素数,n 为 p*q,对于任意 (m1, m2),且 0<=m1< p,0<=m2< q
必然存在一个唯一的m ,0<=m< n,使得
m1 = m mod p
m2 = m mod q
换句话说,给定一个(m1,m2),其满足上述等式的m必定唯一存在。所以解密RSA 的流程 m = c d mod n,可以分解为
m1 = c d mod p
m2 = c d mod q
然后再计算m
但是等式 c d mod p 或者 c d mod q ,模数虽然从n降为p或q了,但是私钥指数指数d还是较大,运算还是比较消耗性能。所以我们还需要降低指数。
仔细看等式 c d mod p
令d = k(p-1) + r 则 c d mod p
= c k(p-1)+r mod p
= c r * c k(p-1) mod p
因为 c (p-1) mod p = 1 (欧拉定理)
= c r mod p
r 是 d 除 p-1 的余数,即 r = d mod (p-1) 所以 c d mod p 可以降阶为 c d mod p-1 mod p,同理,c d mod q可以降阶为 c d mod q-1 mod q。
可令
dp = d mod p-1
dq = d mod q-1
这样 m1 和 m2 就降为
m1 = c dp mod p
m2 = c dq mod q
模数都降的差不多了。想要求解明文,除了上述的 p、q、d、dp、dq,我们还需要 q 对 p 的逆元
qinv = q -1 mod p
结合上述的公式,我们得到最终的明文公式
h = qinv(m1−m2) mod p
m = m2+hq mod p∗q