蓝桥杯 — — RSA解密

RSA解密

友情链接:RSA解密

题目:

思路:

对于这道题目,给出了三个已知量n d C,要我们进行解密,对于解密的公式 X = C e m o d    n X = C^e \mod n X=Cemodn来讲,我们有唯一的参数e是未知的,因此问题就转化为了如何求解e的值,对于求解e的值,题目给出的有用条件:找到两个质数p q使其满足 d = p ⋅ q d = p \cdot q d=p⋅q 和 d m o d    ( p − 1 ) ⋅ ( q − 1 ) = 1 d \mod {(p - 1) \cdot (q - 1)} = 1 dmod(p−1)⋅(q−1)=1,即d(p - 1)·(q - 1)互质,然后给出 ( d ⋅ e ) m o d    ( ( p − 1 ) ⋅ ( q − 1 ) ) = 1 (d\cdot e) \mod ((p - 1)\cdot (q - 1)) = 1 (d⋅e)mod((p−1)⋅(q−1))=1。使用更易懂的公式可表述为: d ⋅ e − k ( ( p − 1 ) ⋅ ( q − 1 ) ) = 1 ,(其中 k 是一个未知量) d\cdot e - k((p - 1)\cdot (q - 1)) = 1,(其中k是一个未知量) d⋅e−k((p−1)⋅(q−1))=1,(其中k是一个未知量),意思是:对于 d ⋅ e d \cdot e d⋅e,可以减去 k k k个 ( p − 1 ) ⋅ ( q − 1 ) (p - 1)\cdot (q - 1) (p−1)⋅(q−1),使得最后的差值为 1 1 1。

对公式 d ⋅ e − k ( ( p − 1 ) ⋅ ( q − 1 ) ) = 1 ,(其中 k 是一个未知量) d\cdot e - k((p - 1)\cdot (q - 1)) = 1,(其中k是一个未知量) d⋅e−k((p−1)⋅(q−1))=1,(其中k是一个未知量) 的理解:

举个例子:假设 d ⋅ e = 16 d \cdot e = 16 d⋅e=16而 ( p − 1 ) ⋅ ( q − 1 ) = 5 (p - 1)\cdot (q - 1) = 5 (p−1)⋅(q−1)=5,我们要求 ( d ⋅ e ) m o d    ( ( p − 1 ) ⋅ ( q − 1 ) ) = 1 即 16 m o d    5 = 1 (d\cdot e) \mod ((p - 1)\cdot (q - 1)) = 1 ~~~ 即16 \mod 5 = 1 (d⋅e)mod((p−1)⋅(q−1))=1 即16mod5=1,这个意思是使16除以5的余数为1,因为除法和减法有着非常大的联系(除以一个数的值等于减去这个数的次数,余数等于相减之后的差值),因此我们可以从新将其写为 16 − k ⋅ 5 = 1 16 - k\cdot 5 = 1 16−k⋅5=1,即16 减去多少个5后值为1

为了方便书写,我们设 φ = ( p − 1 ) ⋅ ( q − 1 ) \varphi = (p - 1)\cdot(q - 1) φ=(p−1)⋅(q−1),这样公式就又化简为了 d ⋅ e − k φ = 1 ,(其中 k 是一个未知量) d\cdot e - k\varphi = 1,(其中k是一个未知量) d⋅e−kφ=1,(其中k是一个未知量),由于又涉及到了两个新的未知量 φ \varphi φ和 k k k,要想求出 e e e,我们需要提前求出 φ \varphi φ和 k k k,对于k,暂时无法直接求出,因为并没有足够的信息能够求出这个值(在下面会使用到扩展欧几里得算法进行求解),对于 φ \varphi φ,其中又包含了两个参数pq,对于pq的求解,我们可以使用枚举的思想,利用给出的公式 d = p ⋅ q d = p \cdot q d=p⋅q和p q都是质数这两个条件进行求解。


求解参数 p p p 和参数 q q q

一个规律是:对于一个数,如果这个数是由两个质数相乘得来的,那么这个数的因子只包含1它本身以及相乘而来的两个质数。如: 35 = 5 × 7 35 = 5 \times 7 35=5×7,那么35的因子有1 35 5 7这四个数。

利用这个规律,我们可以设计出如下算法:

cpp 复制代码
// 寻找pq的值
void Find(long long n, long long &p, long long &q){
	int ans;
	for(long long i = 2;i * i <= n;i ++){
		if(n % i != 0){
			continue;
		}
		p = i;
		q = n / p;
	}
	return ;
} 

如此我们便找到了两个参数pq,接下来便到了真正要求解参数e的时候了我们得到了参数 φ \varphi φ,因此原公式 d ⋅ e − k φ = 1 d\cdot e - k\varphi = 1 d⋅e−kφ=1,只剩下了两个参数k,和我们需要的参数e,这就变为了求解一个二元一次方程组,对于二元一次方程组,要想求出两个未知量,我们至少需要直到两个等式关系,但是这里我们只知道了一组等式关系,因此无法使用常规的思想进行求解。但是观察到等式右边的值为1,如果了解扩展欧几里得算法的小伙伴就会想到,这个方程可以使用扩展欧几里得的方法进行求解。

扩展欧几里得算法

对于扩展欧几里得算法,百度上是这样解释的。

对于公式: a x + b y = gcd ⁡ ( a , b ) ax + by = \gcd(a, b) ax+by=gcd(a,b),gcd(a, b)表示ab的最大公约数。一个理解方式是:如果ab是整数,那么一定存在整数xy,能使得公式 a x + b y = gcd ⁡ ( a , b ) ax + by = \gcd(a, b) ax+by=gcd(a,b)成立。换句话说:如果公式 a x + b y = C ax + by = C ax+by=C成立,那么 C C C一定是 gcd ⁡ ( a , b ) \gcd (a,b) gcd(a,b)的若干倍。

对于扩展欧几里得算法由如下公式:

公式推导:

a 1 ⋅ x 1 + b 1 ⋅ y 1 = gcd ⁡ ( a 1 , b 1 ) a_1 \cdot x_1 + b_1 \cdot y_1 = \gcd (a_1, b_1) a1⋅x1+b1⋅y1=gcd(a1,b1)
a 2 = ( b 1 ) ⋅ x 2 + b 2 = ( a 1 % b 1 ) = ⋅ y 2 = gcd ⁡ ( a 2 , b 2 ) a_2=(b1) \cdot x_2 + b_2 = (a_1 \% b_1) = \cdot y_2 = \gcd (a_2, b_2) a2=(b1)⋅x2+b2=(a1%b1)=⋅y2=gcd(a2,b2)
a 3 = ( b 2 ) ⋅ x 3 + b 3 = ( a 2 % b 2 ) ⋅ y 3 = gcd ⁡ ( a 3 , b 3 ) a_3 = (b2) \cdot x_3 + b_3 = (a_2 \% b2) \cdot y_3 = \gcd (a_3, b_3) a3=(b2)⋅x3+b3=(a2%b2)⋅y3=gcd(a3,b3)
⋮ \vdots ⋮
a i − 1 = ( b i − 2 ) ⋅ x i − 1 + b i − 1 = ( a i − 2 % b i − 2 ) ⋅ y i − 1 = g c d ( a i − 1 , b i − 1 ) a_{i-1} = (b_{i - 2})\cdot x_{i - 1} + b_{i - 1} = (a_{i - 2} \% b_{i - 2}) \cdot y_{i - 1} = gcd(a_{i - 1}, b_{i - 1}) ai−1=(bi−2)⋅xi−1+bi−1=(ai−2%bi−2)⋅yi−1=gcd(ai−1,bi−1)
a i = ( b i − 1 ) ⋅ x i + b i = ( a i − 1 % b i − 1 ) ⋅ y i = g c d ( a i , b i ) a_i = (b_{i - 1})\cdot x_i + b_i = (a_{i - 1} \% b_{i - 1}) \cdot y_i = gcd(a_i, b_i) ai=(bi−1)⋅xi+bi=(ai−1%bi−1)⋅yi=gcd(ai,bi)
⋮ \vdots ⋮
a n − 1 = ( b n − 2 ) ⋅ x n − 1 + b n − 1 = ( a n − 2 % b n − 2 ) ⋅ y n − 1 = g c d ( a n − 1 , b n − 1 ) a_{n-1} = (b_{n - 2})\cdot x_{n - 1} + b_{n - 1} = (a_{n - 2} \% b_{n - 2}) \cdot y_{n - 1} = gcd(a_{n - 1}, b_{n - 1}) an−1=(bn−2)⋅xn−1+bn−1=(an−2%bn−2)⋅yn−1=gcd(an−1,bn−1)
a n = ( b n − 1 ) ⋅ x n + b n = ( a n − 1 % b n − 1 ) ⋅ y n = g c d ( a n , b n ) a_n = (b_{n - 1})\cdot x_n + b_n = (a_{n - 1} \% b_{n - 1}) \cdot y_n = gcd(a_n, b_n) an=(bn−1)⋅xn+bn=(an−1%bn−1)⋅yn=gcd(an,bn)

由欧几里得算法(即:辗转相除法)可以知道: gcd ⁡ ( a i − 1 , b i − 1 ) = gcd ⁡ ( a i , b i ) \gcd (a_{i - 1}, b_{i - 1}) = \gcd (a_i, b_i) gcd(ai−1,bi−1)=gcd(ai,bi),即无论ab的值是什么,其最大公因数永远是相等的。

因此可以得到: a i = ( b i − 1 ) ⋅ x i − 1 + b i = ( a i − 1 % b i − 1 ) ⋅ y i − 1 = a i − 1 = ( b i − 2 ) ⋅ x i + b i − 1 = ( a i − 2 % b i − 2 ) ⋅ y i a_i = (b_{i - 1})\cdot x_{i - 1} + b_i = (a_{i - 1} \% b_{i - 1}) \cdot y_{i - 1} = a_{i-1} = (b_{i - 2})\cdot x_i + b_{i - 1} = (a_{i - 2} \% b_{i - 2}) \cdot y_i ai=(bi−1)⋅xi−1+bi=(ai−1%bi−1)⋅yi−1=ai−1=(bi−2)⋅xi+bi−1=(ai−2%bi−2)⋅yi

因为: a % b = a − a b × b a \% b = a - \frac{a}{b} \times b a%b=a−ba×b,带入到上述公式中得可以得到: a i − 1 ⋅ x i − 1 + b i − 1 ⋅ y i − 1 = b i − 1 ⋅ x i + ( a i − 1 − ( a i − 1 / b i − 1 ) × b i − 1 ) ⋅ y i a_{i - 1} \cdot x_{i - 1} + b_{i - 1} \cdot y_{i - 1} = b_{i - 1} \cdot x_i + (a_{i - 1} - (a_{i - 1} / b_{i - 1}) \times b_{i - 1}) \cdot y_i ai−1⋅xi−1+bi−1⋅yi−1=bi−1⋅xi+(ai−1−(ai−1/bi−1)×bi−1)⋅yi,整理可得: a i − 1 ⋅ x i − 1 + b i − 1 ⋅ y i − 1 = a i − 1 ⋅ y i + b i − 1 ⋅ ( x i − a i − 1 b i − 1 ⋅ y i ) a_{i - 1} \cdot x_{i - 1} + b_{i - 1} \cdot y_{i - 1} = a_{i - 1} \cdot y_i + b_{i - 1} \cdot (x_i - \frac{a_{i - 1}}{b_{i - 1}}\cdot y_i) ai−1⋅xi−1+bi−1⋅yi−1=ai−1⋅yi+bi−1⋅(xi−bi−1ai−1⋅yi)

可以发现如下规律:

x i − 1 = y i x_{i - 1} = y_i xi−1=yi

y i − 1 = x i − a i − 1 b i − 1 ⋅ y i y_{i - 1} = x_i - \frac{a_{i - 1}}{b_{i - 1}}\cdot y_i yi−1=xi−bi−1ai−1⋅yi

并且由欧几里得算法可以得到:最后 b n = 0 b_n = 0 bn=0, a n = g c d ( a , b ) a_n = gcd(a, b) an=gcd(a,b)

如果我们知道了gcd(a, b),那么我们可任意设定 y n y_n yn的值,因为 y n y_n yn的参数 b n = 0 b_n = 0 bn=0,并且设定 x n x_n xn的值为 g c d ( a , b ) gcd(a, b) gcd(a,b),再利用上面得到的规律,可以递归的求解出 x 1 x_1 x1和 y 1 y_1 y1的值。举例:对于 a n = ( b n − 1 ) ⋅ x n + b n = ( a n − 1 % b n − 1 ) ⋅ y n = g c d ( a n , b n ) a_n = (b_{n - 1})\cdot x_n + b_n = (a_{n - 1} \% b_{n - 1}) \cdot y_n = gcd(a_n, b_n) an=(bn−1)⋅xn+bn=(an−1%bn−1)⋅yn=gcd(an,bn),我们可以求解出 x n x_n xn和 y n y_n yn(因为这两个值是设定出来的),然后 x n − 1 = y n y n − 1 = x n − a n − 1 b n − 1 ⋅ y n x_{n - 1} = y_n~~~y_{n - 1} = x_n - \frac{a_{n - 1}}{b_{n - 1}}\cdot y_n xn−1=yn yn−1=xn−bn−1an−1⋅yn,如此便可递归的求解出 x 1 x_1 x1和 y 1 y_1 y1了。

因为公式 d ⋅ e − k φ = 1 d\cdot e - k\varphi = 1 d⋅e−kφ=1中 d d d与 φ \varphi φ是互质的,也就是 gcd ⁡ ( d , φ ) = 1 \gcd(d, \varphi) = 1 gcd(d,φ)=1,正好可以使用扩展欧几里得算法进行求解这个二元一次方程组,相当于已经知道了 gcd ⁡ ( d , φ ) \gcd(d, \varphi) gcd(d,φ)的值, d ⋅ e − k φ = gcd ⁡ ( d , φ ) = 1 d\cdot e - k\varphi = \gcd(d, \varphi) = 1 d⋅e−kφ=gcd(d,φ)=1。

代码实现:

cpp 复制代码
long long ex_gcd(long long a, long long b, long long &x, long long &y){   // 这里的参数x,y一定要用引用的方式传参
	if(!b){  // 如果b == 0, 表明已经递归到最后一个了,就设定y = 0, x = 1(表示a和b的最大公因子)
		x = 1;
		y = 0;
		return a;   // 到达最后一个公式后就进行返回a,a表示的是第一个式子的最大公因子
	}
	long long d = e_gcd(b, a % b, x, y);  // 记录下一个式子的返回的a的值,因为是递归的关系,最终记录的是最后一个式子的a的值。
	long long t = x;   // 使用临时变量记录一下下一个式子得到的x的值
	x = y;  // 更新当前式子的x的值
	y = t - a / b * y;  // 更新当前式子的y的值
	return d;  // 最后返回d,d的值是由最后一个公式的a的值进行传递过来的,即(a, b)的最大公因子
}

如此我们便可以求得ek,的值了,如果上述代码返回的不是1,即(a, b)的最大公因子不是1,说明没有解,即:ab的最大公因子不是1,那么就直接输出-1,如果返回的是1,表明有解,并且已经求出解了,即:e = x, k = y。为了防止e为负数,需要对计算出的结果做如下运算: e = ( e m o d    φ + φ ) m o d    φ e = (e \mod \varphi + \varphi) \mod \varphi e=(emodφ+φ)modφ。

如何理解公式 e = ( e m o d    φ + φ ) m o d    φ e = (e \mod \varphi + \varphi) \mod \varphi e=(emodφ+φ)modφ呢?

通俗的解释:

因为e满足公式 e ⋅ d ≡ 1 m o d    φ ( n ) e\cdot d \equiv 1 \mod \varphi(n) e⋅d≡1modφ(n) ,也就是 e ⋅ d e\cdot d e⋅d与1在模 φ \varphi φ下余数相等,因此为了确保e为正值,我们可以利用公式 e = ( e m o d    φ + φ ) m o d    φ e = (e \mod \varphi + \varphi) \mod \varphi e=(emodφ+φ)modφ来使e保持 0 − φ 0 - \varphi 0−φ的范围内,举个例子:如果 a = -3b = 17m = 5,那么 − 3 ≡ 17 ( m o d 5 ) -3 \equiv 17 \pmod{5} −3≡17(mod5),因为 -3 除以 5 的余数为 217 除以 5 的余数也为 2,为了保证a为正值,我们可以令: ( − 3 m o d    5 + 5 ) m o d    5 = ( 2 + 5 ) m o d 5 = 2 (-3 \mod 5 + 5) \mod 5 = (2 + 5) mod 5 = 2 (−3mod5+5)mod5=(2+5)mod5=2。得到 a = 2 a = 2 a=2,并且a5进行取余得到的结果也为2

较为官方的解释:

保持同余性质 :在RSA算法中,ed 必须满足 e ⋅ d ≡ 1 m o d    φ ( n ) e\cdot d \equiv 1 \mod \varphi(n) e⋅d≡1modφ(n) 表示 e ⋅ d e\cdot d e⋅d与1在模 φ \varphi φ下余数相等,这是加密和解密正确性的基础。如果 e 为负数,直接取模可能会导致 ed 不满足同余性质。通过使用 e = ( e m o d    φ + φ ) m o d    φ e = (e \mod \varphi + \varphi) \mod \varphi e=(emodφ+φ)modφ 这样的方法,可以保持 e 在模 φ \varphi φ 下的同余性质不变,同时确保 e为正值。

在数论中,同余性质是指两个整数除以同一个正整数所得的余数相同。形式上,如果两个整数 ab 除以正整数 m 所得的余数相同,则称 ab 在模 m下同余,记作 a ≡ b ( m o d m ) a \equiv b \pmod{m} a≡b(modm)。例如,如果 a = 7b = 17m = 5,那么 7 ≡ 17 ( m o d 5 ) 7 \equiv 17 \pmod{5} 7≡17(mod5),因为 7 除以 5 的余数为 217 除以 5 的余数也为 2

在求解出e之后,我们可以直接使用公式: X = C e m o d    n X = C^e \mod n X=Cemodn,将值进行带入即可,但是又因为 C C C的值非常大,我们需要用到快速幂的方法,又因为快速幂中存在乘法运算,极易导致内存溢出的现象(在很大的数的情况下),因此我们需要在快速幂的基础上使用快速乘的方法进行运算(在计算机中,计算加法的速度往往比计算乘法的速度要快),并且数值都要使用long long的类型。

代码:

cpp 复制代码
// RSA解密
#include<iostream>
#include<math.h>
#include<vector> 
using namespace std;
typedef long long ll;
// 最大公约数
int gcd(int a, int b){
	return b == 0? a : gcd(b, a % b);
} 

// 扩展欧几里得算法
long long e_gcd(long long a, long long b, long long &x, long long &y){
	if(!b){  // 如果b == 0 
		x = 1;
		y = 0;
		return a;
	}
	long long d = e_gcd(b, a % b, x, y);
	long long t = x;
	x = y;
	y = t - a / b * y;
	return d;
}

// 快速乘算法
ll FastTimes(ll num, ll times, ll n){
	ll ans = 0;    // 这里要初始化为0 
	num %= n;
	while(times){
		if(times & 1){
			ans += num;
			ans %= n;
			times --;
		}else{
			times >>= 1;
			num += num;
			num %= n;
		}
	}
	return ans;
} 

// 快速幂算法
ll FastPower(ll base, ll index, ll n){
	ll ans = 1;
	base %= n;
	while(index){
		if(index & 1){
			ans = FastTimes(ans, base, n);
			index--;
		}else{
			index >>= 1;
			base = FastTimes(base, base, n);
		}
	}
	return ans;
}

// 寻找pq的值
void FindPQ(ll n, ll &p, ll &q){
	int ans;
	for(ll i = 2;i * i <= n;i ++){
		if(n % i != 0){
			continue;
		}
		p = i;
		q = n / p;
	}
	return ;
} 

int solve(){
	ll n = 1001733993063167141;
	ll c = 20190324;
	ll d = 212353;
    ll p = -1, q = -1;
    // FindPQ(n, p, q);  // 找到p q的值
	p=891234941,q=1123984201;
	// 找到p 和 q之后继续寻找e
	ll fy = (p - 1) * (q - 1);
	ll _x, _y;
	ll gc = e_gcd(d, fy, _x, _y);
 	ll e;
	if(gc == 1){
		e = (_x%fy + fy)%fy;
	}else{
		return -1;
	}

	ll X = FastPower(c, e, n);
	cout<<X<<endl;
	return 0;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	ll t = 1; 
	while(t--){
		solve();
	}
	return 0;
} 
相关推荐
做人不要太理性12 分钟前
【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!
c++·哈希算法·散列表·unordered_map·unordered_set
程序员-King.21 分钟前
2、桥接模式
c++·桥接模式
chnming198725 分钟前
STL关联式容器之map
开发语言·c++
VertexGeek27 分钟前
Rust学习(八):异常处理和宏编程:
学习·算法·rust
石小石Orz28 分钟前
Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~
javascript·人工智能·算法
程序伍六七38 分钟前
day16
开发语言·c++
小陈phd1 小时前
Vscode LinuxC++环境配置
linux·c++·vscode
火山口车神丶1 小时前
某车企ASW面试笔试题
c++·matlab
jiao_mrswang1 小时前
leetcode-18-四数之和
算法·leetcode·职场和发展
qystca1 小时前
洛谷 B3637 最长上升子序列 C语言 记忆化搜索->‘正序‘dp
c语言·开发语言·算法