这个东西在打ACM的时候接触过,觉得挺有意思的。
参考:
https://www.cnblogs.com/idreamo/p/9411265.html
https://www.cnblogs.com/coolYuan/p/9168284.html
¶ 写在前面
以下大多数描述的目的是方便理解,不是严格的数学语言(要写成严格的数学语言也太难了orz)。
由于涉及的整数位数不多,所以下文中默认大整数乘法是朴素的大整数乘法,没有用到FFT。
¶ 找到大素数p,q
根据上面的参考文档的说法,至少200位。
可以先生成两个大随机数p'和q',然后一直加1,用Miller Rabin判断是不是素数。
假如从x开始逐个往上找素数,则Miller Rabin是
综上,可以在大约
但是p和q是需要求的值,怎么可以拿它们来作为复杂度的估计呢?要是能知道p和q的上界就好了。
可以先用大量算力求出一个大量级的素数,设为P,只要上文的p'和q'小于P,p和q就不超过P了。这样,找到p和q的时间复杂度就是
¶ 定义n = p * q
大数乘法。复杂度为
¶ 找到e,使得e与(p-1)(q-1)互素
只需要从3开始往上逐个测试素数,直到找到一个不为
由于
(感谢评论区的zzyyyl大佬)由于x以内素数的乘积是
验证时只需检查能否整除
由于e一般比较小,在上述计算方式下,e是
(e,n)当作公钥。
¶ 设
前面规定了e与(p-1)(q-1)互素,所以e有关于(p-1)(q-1)的乘法逆元。可以用拓展欧几里得算法求解。
exgcd的C++代码
template<typename T>
void exgcd(const T& a, const T& b, T& x, T& y) {
if(b == 0) {
= 1;
x = 0;
y return;
}
(b, a%b, x, y);
exgcd= x;
T tmp = y;
x = tmp - a / b * y;
y }
template<typename T>
(const T& num, const T& p) {
T Inv, tmp;
T ans(num, p, ans, tmp);
exgcdreturn (ans % p + p) % p;
}
exgcd迭代深度为
由于进行取逆元运算之后的值的分布比较随机,所以d的量级一般与(p-1)(q-1)的差不多,所以把
(d,n)当作私钥。
¶ 生成密钥复杂度
综上,大约为
¶ 加密
已知原文a,要求密文b,a,b<n
显然b只有一种可能取值。
进行
¶ 解密
同理,复杂度为
所以解密开销比加密高。
下面证明(1)式成立
¶ 引理1
在RSA应用环境下,即使a与n不互素,也有
证明:
由这篇文章知即使a与n不互素,也有
所以
由数学归纳法知
¶ 定理
证明:
因为
所以
即
所以
证毕
¶ 数字签名
参考:https://baike.baidu.com/item/%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D/212550?fr=aladdin
e与d互为关于
在RSA通信过程中,把(e,n)作为公钥的原因无非是因为e一般比较短,所以可以公开。如果把d公开了,用(e,n)作为私钥,由于n是公开的,e又比较小,攻击者很容易就能枚举出e来。
由于e与d在数学上等价,所以可以把d作为数字签名的私钥,把文件加密,别人验证时用公钥e解密,如果成功说明文件确实是用d加密的。
¶ ssl证书
ssl证书可以验证连接上的服务器是否为真的服务器。数字签名就是ssl证书的基础。
简化流程如下:
- 第三方可信机构(例如CA)用自己的私钥加密了服务器证书,然后把公钥公开。
- 客户端向服务器索要证书
- 服务器返回给客户端自己的证书
- 客户端用第三方可信机构提供的公钥解密证书,发现解密成功,说明该证书确实是用该机构的私钥加密的
- 证书里有该服务器的公钥,客户端想要验证服务器是不是真的有这个公钥对应的私钥,于是客户端生成一个随机数,用证书里的公钥加密,然后发给服务器
- 服务器用自己的私钥解开,然后发给客户端
- 客户端收到了正确的随机数,就知道现在跟自己通信的服务器是真的,就放心通信了。
orz