您的位置:首页 > 其它

CTFCrypto练习之RSA算法

2017-01-15 16:38 246 查看
简介

RSA属于非对称加密算法,因为RSA使用了两个不同的密钥分别用于加密和解密,这两个密钥称之为公私钥对,其中公钥用于加密,且公钥是公开的,而私钥用于解密,私钥是私有的。

在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK(对于简答的加密的处理的话还是可以破解的)。

RSA加密过程

找到两个大素数p和q,计算出n=p×q

得到φ=(p−1)×(q−1),然后选择一个e(1<e<φ),且gcd(φ,e)=1,gcd为最大公约数,即e和φ互质(互为质数)

计算出d,计算方法:(e×d)%φ=1

得到了公私钥对,其中{e,n}为公钥,{d,n}为私钥。

针对明文M,进行加密:C=Me%n,得到的C即为密文

针对密文C,进行解密,M=Cd%n,得到的M即为明文

通过上面的加密方式和解密方式可以知道,明文是以数字的方式进行加密和解密,一般都是针对单个字符的ASCII码进行加密,也可能两个…,这里的操作对象为位数小于n的位数的数据,比如说一串数据变为二进制为111011101101,而n的位数为5那么你要处理的数据可能是1位二进制,2位二进制,3位二进制和4位二进制

图如下:



接下来的详细分析要进行一些知识讲解

欧几里得算法和扩展欧几里得算法(当然其中素数和质数之类的知识百度一下就知道了),在本人的博客中有写,这是地址:数学基础讲解

简单的破解RSA算法

之前也说了,RSA的公钥是公开的,即对于知道{e,n}的数据,如果n比较小的话我们是可以暴力出结果的,这就是所谓的数学暴力攻击

攻击方法一般是:给定RSA的公钥{e,n},根据RSA的定义,如果能够将n分解为两个素数的乘积,即n=p×q,那么就可以计算出d了,也就是得到私钥{d,n}

所以这种方法只能针对n的值非常小的情况下,基本最大就是108大小的,这应该就是n可以暴力的极限了,二进制位数为32位。

举个例子

已知n,e,我们可以暴力查找质数,从而得到φ=(p−1)×(q−1),然后针对(e×d)%φ=1求出d来。

这里就可以用扩展欧几里得算法来求解,基本求解方法本人博客中也提到了,这里简单提醒一下

令d为x

(1)(e×x)%φ=1

(2)e×x=φ×y+1

(3)e×x−φ×y=1

(4)e×x+φ×y=1

所以就是求解x的值,如果求出的x为负数,就需要不断的调整将它变为正数,这些知识点在本人讲扩展欧几里的算法的时候都已经详细介绍了,这里稍微提一下:

如果x<0,那么将式子变为e×(x+φ)+φ×(y+e)=1

然后令x=x+k×φ,y=y+e×k,原式变为e×x+φ×y=1,变回了原来的式子,这就是变为正数的方法,其实就是在一条直线上面来回移动x轴上的点将它变为正整数就可以了

所以这里可以简化,但我们求出的值为一个负数的时候我们只需要将x变为正数不需要管y的值,所以x=x+k×φ,所以我们写个循环判断加φ直到x为正数

while(x < 0){
x += φ
}


简单破解例题讲解

已知公钥为{49,851}以及RSA密文数据[617,673,319,695,16,252,299,319,657,346,767,299,346,88],请还原对应的明文

通过上述说的步骤我们可以得到如下数据:

n=37∗23=851

φ=36∗22=792

e=49

其中n=p×q,p=37,q=23的求解方式为for循环暴力:

//其中prime是一个素数的表,isprime也是一个素数的表用于判断是否为素数
for(int i = 1;i < len;i ++){
if(n % prime[i] == 0 && isprime[n/prime[i]]){
//输出prime[i]和n/prime[i]
break;
}
}


c++代码算出d的值

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

LL exgcd(LL a, LL b, LL &x, LL &y) {
if(b == 0) {
x = 1; y = 0;
return a;
}
LL r = exgcd(b, a % b, x, y);
LL t = y;
y = x - a / b * y;
x = t;
return r;
}

/*可以得到x>=bound时的x和y,返回true表示有解*/
bool solve(LL a, LL b, LL c, LL bound, LL &x, LL &y) {
LL xx, yy, d = exgcd(a, b, xx, yy);
if(c % d) return false;

xx = xx * c / d; yy = yy * c / d;
LL t = (bound - xx) * d / b;

x = xx + b / d * t;
if(x < bound) {
t++;
x = xx + b / d * t;
}
y = yy - a / d * t;
return true;
}

int main(){
LL k = 792, e = 49;
LL x, y;
solve(e, k, 1, 0, x, y);
printf("%lld\n", x);
return 0;
}


这里用了一个c++求解扩展欧几里的算法结果的模板,solve()和exgcd()这两个函数,给出这个函数运用地址:http://blog.csdn.net/qwb492859377/article/details/47679225

接下来是python代码,这个代码就是用上面是说的怎么让x为正数

#! /usr/bin/env python
# -*- coding=utf-8 -*-

import math

#扩展欧几里得算法求解
def exgcd(a, b):
if a < b:
return exgcd(b, a)

if b == 0:
return a , 1, 0
gcd , x, y = exgcd(b, a % b)
return gcd, y, x - a / b * y

#求解d的值
def getd():
k = 792
e = 49
gcd, y, x = exgcd(k, e)
while x < 0:
x += k
return x

if __name__ == '__main__':
print getd()


得到d=97,然后我们写破解代码

c++破解代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;
//快速幂,知识点数学基础博客中有
/*x^n%mod*/
LL pow_LL(LL x, LL n, LL mod){
LL ret = 1;
while(n > 0){
if(n & 1) ret = ret * x % mod;
x = x * x % mod;
n >>= 1;
}
return ret;
}

int main(){
LL d = 97, n = 851;
LL arr[] = {617,673,319,695,16,252,299,319,657,346,767,299,346,88};
for(int i = 0;i < 14;i ++){
printf("%c", pow_LL(arr[i], d, n));
}
return 0;
}


python代码

#! /usr/bin/env python
# -*- coding=utf-8 -*-

import math

def pow_LL(x, n, mod):
ret = 1
while n > 0:
if (n & 1) > 0:
ret = (ret * x) % mod
x = (x * x) % mod
n >>= 1
return ret

def test():
d = 97
n = 851
arr = [617,673,319,695,16,252,299,319,657,346,767,299,346,88]
new_arr = []
for i in arr:
new_arr.append(chr(pow_LL(i, d, n)))
print "".join(new_arr)
if __name__ == '__main__':
test()


c++执行代码结果



python执行代码结果

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: