7 - RSA 算法
RSA 算法
原书:《Understanding Cryptography: A Text book for Students and Practitioners》
在 Whitfield Diffie 与 Martin Hellman 于 1976 年发表关于公钥加密的论文之后,掀起了公钥加密研究热潮。在 1977 年,Ronald Rivest,Adi Shamir 以及 Leonard Adleman 提出了一种被广泛使用的非对称加密策略,并以他们三个人的名字缩写名命为 RSA。
7.1 介绍
RSA 具有很多应用,实际上最常用的功能是:
- 加密小批量数据,比如进行密钥传输
- 数字签名,这将在第十章介绍,做为网络上的数字证书
需要注意的是,RSA 加密并不能够完全替代对称加密,因为相较于对称加密算法,RSA 要慢上数倍。这是因为 RSA 中涉及很多计算。因此它主要用在交换对称运算的密钥上。实际上,RSA 通常与对称加密运算如 AES 一起使用,在这个组合中,实际进行块数据加密的是 AES 算法。
RSA 所蕴含的单向函数是,对整数的因数分解问题:对两个大的质数做乘法是十分简单的,但是对这两个质数的积进行因数分解则十分困难。欧拉定理以及欧拉 phi 函数在 RSA 算法中扮演着重要的角色。
7.2 加密与解密
RSA 加密与解密,是在整数环 \(Z_n\) 中完成的,模计算是它的核心。RSA 加密原文 \(x\),这个原文是 \(Z_n = {0,1,...,n-1}\) 中的元素。因此原文 \(x\) 的二进制值必须小于 \(n\)。密文同样符合这一条件。使用公钥加密及使用私钥解密可以表示为:
RSA 加密 给定公钥 \((n,e) = k_{pub}\) 及原文 \(x\),加密函数表示为:
其中 \(x,y \in Z_n\)。
RSA 解密 给定私钥 \(d = k_{pr}\) 及密文 \(y\),解密函数可表示为:
其中 \(x,y \in Z_n\)。
实际上,\(x,y,n,d\) 都是十分长的数字,通常有 1024 位长,甚至更长。\(e\) 为加密指数或公钥指数,私钥 \(d\) 为解密指数或私钥指数。如果发送者希望发送一条加密信息给接收者,发送者需要使用接收者发送给他的公钥 \((n,e)\) 加密,接收者则使用他的私钥 \(d\) 进行解密。
即便现在还不知道算法细节,我们已经可以提出 RSA 算法需要满足下面的需求:
- 因为攻击者很容易获得公钥,那么从公钥 \((n,e)\) 计算出私钥的过程应该是极其困难的
- 取决于可用的原文空间与密文空间,加密的原文长度是受限的
- 进行加密与解密的计算过程要相对容易实现,这意味着我们需要一种方法来快速计算大数值指数运算
- 给定 \(n\),应该有许多公私钥对,否则攻击者可以很轻松进行暴力破解
7.3 密钥生成与正确性证明
RSA 密钥生成
输出:公钥 \(k_{pub} = (n,e)\),私钥 \(k_{pr} = (d)\)
- 选择两个大的质数 \(p\) 与 \(q\)
- 计算 \(n = p\cdot q\)
- 计算 \(\Phi (n) = (p-1)(q-1)\)
- 选择公钥指数 \(e \in \{1,2,...,\Phi(n) - 1\}\) 从而有 \(gcd(e,\Phi(n)) = 1\)
- 计算密钥 \(d\),从而有 \(d\cdot e \equiv 1 \quad mod \quad \Phi(n)\)
\(gcd(e,\Phi(n)) = 1\) 条件能够确保 \(e\) 的逆模 \(\Phi(n)\) 存在,保证总有私钥 \(d\) 存在。
密钥 \(d\) 与 \(e\) 可以使用扩展欧几里得算法 EEA 实现。在实践中,通常首先在 \(0 < e < \Phi(n)\) 范围内选择一个公钥参数 \(e\)。我们使用 EEA 算法,并使用输入参数 \(n\) 与 \(e\),获得下面的关系:
如果 \(gcd(e,\Phi(n)) = 1\),我们知道 \(e\) 是一个有效公钥。我们也知道,由扩展欧几里得算法计算的参数 \(t\) 是 \(e\) 的逆,因此:
如果 \(e\) 以及 \(\Phi(n)\) 不是互质关系,我们可以简单选择一个新的 \(e\) 值,并重复上面的过程。注意到 EEA 的系数 \(s\) 在 RSA 算法中是不需要的,因此无需计算。
例 7.1 发送者希望发送接密文信息给接收者,接收者首先通过上面的步骤计算他的 RSA 参数,然后,接收者将公钥发送给发送者,发送者使用公钥加密信息 \(x = 4\),并发送密文 \(y\) 给接收者,接收者使用他的私钥解密 \(y\)。
接收者计算公私钥对
- 选择 \(p = 3,q = 11\)
- \(n = p\cdot q = 33\)
- \(\Phi(n) = (3-1)(11-1)\) = 20
- 选择 \(e = 3\)
- \(d \equiv e^{-1} \equiv 7 \quad mod \quad 20\)
计算得到 \(k_{pub} = (33,3)\),私钥 \(d = 7\)。
发送者使用公钥加密
加密,\(y = x^e \equiv 4^3 \equiv 31 \quad mod \quad 33\),得到密文 \(y = 31\)。
接收者使用私钥解密
解密,\(y^d = 31 ^7 \equiv 4 = x \quad mod \quad 33\),得到原文 \(x = 4\)。
注意到,私钥与公钥满足条件 \(e\cdot d = 3\cdot 7 \equiv 1 \quad mod \quad \Phi(n)\)。
在实际应用中,RSA 的参数是十分大的,模 \(n\) 通常至少有 1024 位长,导致 \(p\)、\(q\) 位长可达 512。
有趣的是,信息 \(x\) 在加密时会进行 \(e\) 阶指数运算,而解密时又会进行 \(d\) 阶指数运算,而得到的结果能回到原文 \(x\)。这个过程可以表示为:
这是 RSA 的关键。现在我们需要证明一下为什么 RSA 能够工作。
证明. 我们需要证明解密加密的逆,\(d_{k_{pr}}(e_{k_{pub}}(x)) = x\)。在我们构建公私钥对时,遵从了 \(d\cdot e \equiv 1 \quad mod \quad \Phi(x)\),通过模操作的定义,这个等式等效于:
\(t\) 为整数,因此有:
使用欧拉定理,如果 \(gcd(x,n) = 1\) 那么 \(1 \equiv x^{\Phi(n)} \quad mod \quad n\),马上有:
我们划分成两个情况:
-
\(gcd(x,n) = 1\)
这一条件下,满足欧拉定理,因此有:
\[d_{k_{pr}}(y) \equiv (x^{\Phi(n)})^t \cdot x \equiv 1\cdot x \equiv x \quad mod \quad n \]这里证明了,只要原文 \(x\) 与 \(n\) 互质,那么解密函数就是加密函数的逆。
-
\(gcd(x,n) = gcd(x,p\cdot q) \neq 1\)
因为 \(p\) 与 \(q\) 互质,那么 \(x\) 必然以它们二者之一做为因子:
\[x = r\cdot p \\ x = s\cdot q \]其中 \(r,s\) 且 \(r <q,s<p\),不失一般性,我们可以设 \(x = r\cdot p\),那么有 \(gcd(x,q) = 1\),欧拉定理满足:
\[1 \equiv 1^t \equiv (x^{\Phi(q)})^t \quad mod \quad q \]其中 \(t\) 为正整数,有:
\[(x^{\Phi(n)})^t \equiv (x^{(q-1)(p-1)})^t \equiv ((x^{\Phi(q)})^t)^{p-1} \equiv 1^{p-1} = 1 \quad mod \quad q \]使用模操作的定义,这个等效于:
\[(x^{\Phi(n)})^t = 1 + u\cdot q \]其中 \(u\) 是整数,那么有:
\[x\cdot(x^{\Phi(n)})^t = x + x \cdot u\cdot q \\ = x + (r\cdot p)\cdot u \cdot q \\ = x + r\cdot u \cdot (p\cdot q) \\ = x + r\cdot u \cdot n \\ x\cdot (x^{\Phi(n)})^t \equiv x \quad mod \quad n \]可以得到:
\[d_{k_{pr}} = (x^{\Phi(n)})^t \cdot x \equiv x \quad mod \quad n \]
证明完毕。
7.4 加密与解密:快速取幂
公钥算法都是基于超大的数值,如果我们不注意算法实现,那么缓慢地计算速度将会令应用变得不切实际。如果我们查看 RSA 算法的加密与解密运算,我们可以看到,它们都是基于指数模运算,可以表示为:
指数运算直观表示为:
RSA 算法中的指数 \(e\) 与 \(d\) 都是非常大的数值,通常是 1024 - 3072 位长,甚至更长(公钥 \(e\) 有时选择比较小的数值,但是私钥 \(d\) 肯定是非常长的)。用上面直观表示的指数运算,需要至少 \(2^{1024}\) 次乘法。可观测宇宙的原子数量是 \(10^{300}\),为了一次加密会话计算 \(2^{1024}\) 次乘法,那显然是疯狂的。那么,是否有快速计算指数的方法呢?答案当然是肯定的,否则我们只能抛弃 RSA 算法了。其中一个方法是重复平方乘算法。在展示真正的算法之前,我们先用几个简单的例子阐述这个算法。
例 7.2 让我们看一下计算 \(x^8\),使用连乘方法有:
不过我们可以更快速实现结果:
这个方法是快速的,但是限制在指数为 2 的指数,既 \(e\) 及 \(d\) 是 \(2^i\) 形式。
例 7.3 这次我们取一般指数 26,我们希望计算 \(x^{26}\),可以使用下面的方法快速计算:
查看上面的例子,我们可以通过执行两个基础的操作实现最终的结果:
- 计算当前结果的平方
- 当前的结果与基元素 \(x\) 相乘
在上面的例子中,我们执行的顺序是,平方,乘法,平方,平方,乘法,平方,然而在其他的指数计算中,我们不知道具体的平方与乘法的序列。在重复平方乘算法中,提供了一种系统性的方法来查找平方与乘法的序列。简而言之,算法工作如下:
算法扫描最高位到最低位。在每次迭代中,对于每一个指数位,都对当前结果做平方,当且仅当当前扫描的指数位为 1,会在平方后面跟一个乘法。
例7.4 再次考虑 \(x^{26}\),对于重复平方乘法:
算法扫描指数的位,从最高位(最左侧)的 \(h_4\) 开始,到最低位(最右侧) \(h_0\) 结束:
-
\(x = x^1\),初始化,\(h_4 = 1\)
-
\(h_3 = 1\),先平方再乘法
a. \((x^1)^2 = x^2 = x^{10_2}\)
b. \((x^2)\cdot x = x^3 = x^{10_2}x^{1_2} = x^{11_2}\)
-
\(h_2 = 0\),只做平方
a. \((x^3)^2 = x^6 = (x^{11_2})^2 = x^{110_2}\)
-
\(h_1 = 1\),先平方再乘法
a. \((x^6)^2 = x^{12} = (x^{110_2})^2 = x^{1100_2}\)
b. \(x^{12}\cdot x = x^{13} = x^{1100_2}x^{1_2} = x^{1101_2}\)
-
\(h_0 = 0\),只做平方
a. \((x^{13})^2 = x^{26} = (x^{1101_2})^2 = x^{11010_2}\)
下面是这个算法的伪代码:
指数模运算的重复平方乘算法
输入:
基元素 \(x\)
指数 \(H = \sum_{i=0}^th_i2^i\),其中 \(h_i \in 0,1,h_t = 1\)
输出:\(x^H \mod n\)
初始化:\(r = x\)
算法:
for i = t-1 downto 0
r = r^2 mod n
if h_i = 1
r = r*x mod n
return(r)
例 7.5 对于 1024 位的指数,重复平方乘算法平均需要多少次计算呢?
直接的连乘操作需要 \(2^{1024} \approx 10^{300}\) 次乘法操作,使用重复平方乘算法平均只需要 \(1.5 \cdot 1024 = 1536\) 次平方与乘法操作。这在现在计算机上是可以实现的。
7.5 RSA 加速技术
RSA 算法使用的指数数值十分长。即便底层使用了重复平方乘算法,执行一个完全体的 RSA 1024 位指数或超过这个位数的指数运算,计算量都是比较大的。因此,需要一种加速计数,这里我们介绍两种最流行的加速技术。
7.5.1 用短公钥指数快速加密
在这个情况下,公钥 \(e\) 可以选一个比较小的数值,在实践中,三个值 \(e = 3,e = 17,e = 2^{16} + 1\) 是十分重要的,使用这些公钥的复杂性在下表列出:
公钥 \(e\) | \(e\) 的二进制表示 | 平方与乘法操作数 |
---|---|---|
\(3\) | \(11_2\) | 3 |
\(17\) | \(10001_2\) | 5 |
\(2^{16} + 1\) | \(10000000000000001_2\) | 17 |
有趣的是,即便选择这些小数值的公钥,RSA 依旧是安全的。公钥 \(d\) 依然是完全体的,凡是涉及到私钥 \(d\) 的运算,比如解密与签名生成,都是十分缓慢的。其他公钥算法,尤其是椭圆曲线算法,更加快速。
7.5.2 中国剩余定理快速解密
我们不能在不妥协 RSA 安全性的前提下,缩短私钥的长度。如果我们选择一个短的私钥 \(d\),那么攻击者可以简单暴力破解所有可能的数值。通常在实现时,\(e\) 可以选得足够短而 \(d\) 要选完全体长度。
我们的目标是高效实现 \(x^d \mod n\)。首先要注意的是,拥有私钥 \(d\) 的人,同时也会拥有质数 \(p\) 与 \(q\)。中国剩余定理 CRT: Chinese Remainder Theorem
,将长的质数模运算,分成两个短质数 \(p\) 与 \(q\) 的指数模运算。这会涉及三个步骤。
将输入转换为 CRT 域
我们首先将 \(x\) 使用质数 \(p\) 与 \(q\) 变为:
在 CRT 域中进行指数运算
我们进行下面的两个质数运算:
其中两个指数为:
因为两个质数 \(p\) 与 \(q\) 在实践中是相同长度的质数,\(y_p\) 与 \(y_q\) 约是 \(n\) 长度的一半。
转换回到问题域
最后,使用下面的方法组合上面的结果 \((y_p,y_q)\):
其中系数 \(c_p\) 以及 \(c_q\) 计算如下:
因为给定 RSA 实现,质数不会频繁变化,上面的系数可以提前计算。
例 7.6 令 RSA 参数如下:
现在我们使用 CRT 算法计算密文 \(y = 15\) 的解密,\(y^d = 15 ^103 \quad mod \quad 143\)。
第一步,我们计算 \(y\) 的模表示:
第二步,我们在转换域中进行质数运算:
下面是指数:
最后一步,我们要从 \((x_p,x_q)\) 中计算得到 \(x\),因此我们需要系数:
原文 \(x\) 可计算为:
我们现在建立可计算的 CRT 方法。我们重新描述 CRT 指数计算:
假设 \(n\) 具有 \(t+1\) 位,\(p\) 与 \(q\) 约为 \(t/2\) 位长。所有在 CRT 中涉及的数值,\(x_p,x_q,d_p,d_q\),长度都被 \(p,q\) 约束,因此都约 \(t/2\) 位长。如果我们对两个指数运算使用重复平方乘算法,大约需要 \(1.5t/2\) 次平方与乘模运算,二者共计约 \(1.5t\) 次运算。
这与不适用 CRT 算法的常规质数运算的计算复杂度相同。 不过,乘法与平方的数值都只有 \(t/2\) 位,这就与没有 CRT 算法的操作不同了。
7.6 查找大质数
我们还没有讨论 RSA 密钥生成的第一步,生成质数 \(p\) 与 \(q\)。因为 \(n = p\cdot q\),两个质数的长度需要是 \(n\) 位数的一半,比如,如果我们希望 RSA 具有 \(log_2n = 1024\) 特性,那么 \(p\) 与 \(q\) 需要具有 512 位。生成方式是,先生成随机整数,之后再检查数值是否为质数。
为了能够实际应用,我们需要回答两个问题:
- 在我们查找到质数之前,我们需要测试多少个整数?
- 能够以多快的速度检测一个随机整数是否是质数?
7.6.1 质数多否
随机选择一个整数 \(p\) 是一个质数的概率遵从 \(1/ln(p)\) 概率,实际上,我们只需要检测奇数是否是质数,因此一个随机数是质数的可能性是:
例 7.7 对于需要有 512 位 \(p\) 与 \(q\) 的 RSA 算法,\(p,q \approx 2^{512}\),一个随机的奇数是质数的概率为:
这意味着,我们可以期望选择 177 个随机奇数,就可能找到一个质数。
基于上面的函数,整数是质数的概率下降是缓慢的,这意味着,即便是十分长的 RSA 参数,质数的密度依然是很高的。
7.6.2 质数测试
我们需要做的另一项任务,是确定一个整数 \(p\) 是否是质数。我们马上想到的是对数值进行因数分解。然而在 RSA 算法中使用的数值都是超大的数值,因数分解变得不切实际。我们其实并不关注 \(p\) 的因数分解,只需要关注这个数值是否是质数。这样问题就变得简单一点。可以使用费马质数测试或 Miller-Rabin 测试或是它们的变体。
费马质数测试
输入:质数替补 \(p\) 以及安全参数 \(s\)
输出:\(p\) 是否可能是质数
算法:
FOR i = 1 TO s
choose random a in {2, 3, ... , p - 2}
IF a^{p-1} != 1
RETURN(p 是合数)
RETURN(p 可能是质数)
费马质数测试方法依赖于所有质数都遵循的费马定理。如果一个数值 \(a^{p-1} !\equiv 1\) 那么这个数就是合数,但是它的逆命题并不成立,存在合数满足 \(a^{p-1} \equiv 1\),为了探测它们,算法需要使用多个 \(a\) 运行 \(s\) 次。
不幸的是,有一些合数经过多个 \(a\) 的测试之后,依然表现得像个质数,这些数是卡麦尔数。给定一个卡麦尔数 \(C\),对于所有满足 \(gcd(a,C) = 1\) 的整数 \(a\),有:
这样的合数是非常少的,比如在 \(10^{15}\) 以下,一共有 100000 个卡麦尔数。
例 7.8 卡麦尔数
\(n = 561 = 3 \cdot 11 \cdot 17\) 是一个卡麦尔数,因为对于所有 \(gcd(a,561) = 1\),有:
如果卡麦尔数的所有质因数都非常大,只有很少的 \(a\) 能够通过费马测试检测出这个数是合数。
Miller-Rabin 素数测试
定理 7.6.1 给定一个奇质数候选者 \(p\) 的分解:
其中 \(r\) 是奇数,如果我们能够找到一个整数 \(a\),使得对于所有的 \(j = \{0,1,...,u-1\}\),满足:
那么 \(p\) 是一个合数,否则 \(p\) 可能是一个质数。
输入:质数候补 \(p\) 且 \(p - 1 = 2^ur\) 以及安全参数 \(s\)
输出:\(p\) 是否可能是质数
算法:
FOR i = 1 TO s
choose random a in {2, 3, ... , p - 2}
z = a^r mod p
IF z != 1 and z != p - 1
FOR j = 1 TO u - 1
z = z^2 mod p
IF z = 1
RETURN(p 是合数)
IF z != p - 1
RETURN(p 是合数)
RETURN(p 可能是质数)
这个算法依然会有合数 \(p\) 被判断为质数的情况发生。但是,只要我们使用不同的随机基数 \(a\) 多跑几遍,这个误判的概率就会显著降低。运行的次数基于安全参数 \(s\)。
下表展示了确保合数被误判为质数的概率低于 \(2^{-80}\),分别需要多少 \(s\) 参数:
\(p\) 位数 | 安全参数 \(s\) |
---|---|
250 | 11 |
300 | 9 |
400 | 6 |
500 | 5 |
600 | 3 |
例 7.9 Miller-Rabin 测试
令 \(p = 91\),将 \(p\) 写作 \(p - 1 = 2^1 \cdot 45\),我们选择一个安全参数 \(s = 4\),现在选择 \(s\) 次随机数 \(a\):
- \(a = 12,z = 12^{45} \equiv 90 \ mod \ 91\),因此 \(p\) 可能是一个质数
- \(a = 17,z = 17^{45} \equiv 90 \ mod \ 91\),因此 \(p\) 可能是一个质数
- \(a = 38,z = 38^{45} \equiv 90 \ mod \ 91\),因此 \(p\) 可能是一个质数
- \(a = 39,z = 39^{45} \equiv 78 \ mod \ 91\),因此 \(p\) 是一个合数
可以看到 12,17,38 都给出了错误判定。
7.7 实际的 RSA:填充
我们前面介绍的,是仅停留在课本中的 RSA 算法,具有很多弱点。实际的 RSA 算法必须使用填充策略。填充策略是十分重要的,如果没有合适地使用填充策略,那么 RSA 实现可能并不安全。下面列举出前面提到的 RSA 算法中可能有的问题:
- RSA 加密是确定性的,既选定一个密钥,原文总是映射到指定的密文。攻击者可以从密文中获取到这种静态属性
- 原文 \(x = 0,x = 1,x = -1\) 产生的密文等于 \(0,1,-1\)
- 小的公钥质数 \(e\) 以及小的原文 \(x\),如果没有填充或只进行了弱填充,那么极易受到攻击,不过即便对小的公钥质数 \(e\),也没有已知的攻击手段
RSA 是可再塑的。如果攻击者可以将密文转换成其他密文,而解密出的原文是有意义的,那么运算策略被称作是可再塑的。注意到,攻击者不需要解密密文,只需要以一个可预测的形式进行操作即可。如果攻击者将密文 \(y\) 替换为 \(s^ey\),其中 \(s\) 是某个正整数,如果接收者解密接收到的更改后的密文,得到:
即便攻击者并没有解密密文,这样的修改也是有害的。比如,如果 \(x\) 是要发送的金额,那么选择 \(s = 2\),攻击者可以将这个金额翻倍。
所有这些问题的解决方法是使用填充,在进行加密之前,将一个随机结构嵌入到原文中,可以避免上面的情况发生。现在的策略比如 OAEP: Optimal Asymmetric Encryption Padding
是 PKCS #1: Public Key Cryptography Standard #1
的规范。
假设 \(M\) 是待填充的信息,令 \(k\) 做为 \(n\) 字节长度的模数。令 \(|H|\) 做为哈希函数输出的字节长度,令 \(|M|\) 做为信息的字节长度。哈希函数计算出每一个输入信息的固定长度摘要(160/256位)。令 \(L\) 是信息的可选标识(默认空字串)。使用最新的 PKCS #2 (v2.1)
,在 RSA 加密中使用填充策略通过如下方式实现:
-
生成长度 \(k-|M|-2|H| -2\) 归零字串 \(PS\),\(PS\) 的长度可能是 0
-
将 \(Hash[L],PS\) 以及十六进制的值 0x01 以及信息 \(M\) 串到一起,形成一个 \(k-|H|-1\) 字节长度的信息块 \(DB\)
\[DB = Hash(L)||PS||0x01||M \] -
生成长度为 \(|H|\) 的随机字串种子
-
令 \(dbMask = MGF(seed,k-|H|-1)\),其中 \(MGF\) 是掩码生成函数,实际上,哈希函数如 SHA-1 常用作 \(MFG\)
-
令掩码后的数据 \(maskedDB = DB \oplus dbMask\)
-
令种子掩码 \(seedMask = MGF(maskedDB,|H|)\)
-
令掩码后的种子 \(maskedSeed = seed\oplus seedMask\)
-
将掩码后的种子与掩码后的数据与单个字节 0x00 串在一起,生成长度为 \(k\) 字节的编码后的信息 \(EM\):
\[EM = 0x00||maskedSeed||maskedDB \]
在解密侧,校验解密后的信息结构。比如,如果没有分割 \(PS\) 以及 \(M\) 的 0x01,那么会生成解密错误。任何情况的解密错误都不会提供原文有关的信息。
7.8 攻击
自从 RSA 算法在 1977 年提出到现在,已经有数不清的对 RSA 的攻击。但是都没能从根本上撼动 RSA 算法本身的安全性。对 RSA 的攻击主要有下面机中方式:
- 协议攻击
- 数学攻击
- 边带攻击
下面,我们简单点评一下这些攻击方式。
协议攻击
攻击者攻击某个 RSA 算法实现,比如前面提到的可再塑性。可以通过填充避免被攻击。
数学攻击
已知对 RSA 的最好的攻击手段是对模数进行因数分解。如果攻击者知道模数 \(n\),公钥 \(e\) 以及密文 \(y\)。他的目标是计算私钥 \(d\),这个私钥具有属性 \(e\cdot d \equiv 1 \ mod \ \Phi(n)\)。似乎只要他使用扩展欧拉算法就能计算得到 \(d\),但是现实是,他并不知道 \(\Phi(n)\) 的值。这时来到了因数分解,如果攻击者能够解出 \(n\) 得到质数 \(p\) 与 \(q\),如果攻击者能够实现这个目标,那么解密简单异常:
为了防止攻击者实现这样的攻击,模数必须足够大,这也是为什么 RSA 算法的模数在 1024 位或更长。
边带攻击
比如功耗轨迹。详见原文。
7.9 软件与硬件实现
先看一下运算难度,假设我们使用 2048 位的模数,解密时我们使用完全长度的私钥,大概需要 3072 次平方与乘法,每一个都会涉及 2048 位的操作数。假设我们使用 32 位的 CPU,操作数可以使用 64 个寄存器表示,单次乘法需要进行 \(64^2 = 4096\) 次乘法操作,因为我们需要对两个操作数的寄存器做乘法。另外,我们还需要进行模运算,最好的算法也需要 \(64^2 = 4096\) 次乘操作。因此,单次乘操作 CPU 要进行 \(4096 + 4096 = 8192\) 次整数乘法操作。因为我们有 3072 次运算,单次解密需要进行的乘操作是:
这显然是一个很大的计算开销,其他公钥策略也有相似的复杂度。
如果使用高性能 VLSI
芯片,可以在高速硬件上以 100us 实现 RSA 操作。
如果以软件实现,在现今的 2 GHz CPU 上,对 2048 位 RSA 需要约 10ms 进行一次解密运算,这样的带宽约为 204800 bps。相比于现在的网速,这显然是很慢的速度。因此 RSA 并不怎么用作块数据加密。