基于boost大数库的RSA加密算法实现

Github:RSA加算法项目超链接

目录

密钥

对称加密&&非对称加密

RSA加密方式

数学基础

密钥产生过程

快速幂取模运算

进阶之路


  • 密钥

它是在明文转换为密文(加密)或将密文转换为明文(解密)的算法中输入的参数。密钥分为对称密钥与非对称密钥。就像是保险柜的密码,相同的密码进行加锁,要打开就要相同的密码。

密钥有公钥和私钥。公钥是公开的,私钥是私有的。

  • 对称加密&&非对称加密

对称加密:就像是双方共同遵守一种协议,通过一种规则对消息进行加密,然后有通过这个规则进行解密。这个规则便是密钥,也可以叫私钥。双方拿着这一把钥匙加密解密。

这种方式的优点就是速度快,密钥的保存与传递难以管理。

非对称加密:在对称加密的基础上增加了一把公钥。双方都会生成一对公钥和私钥,公钥是公开的,公钥用来加密,私钥用来解密。当甲乙双方要进行加密通信时,甲方就用乙方公开的公钥加密,乙方获得密文后拿自己的私钥解密;同理,乙方用甲方公开的公钥加密,甲方获得密文后拿自己的私钥解密。

1977年,三位数学家RivestShamir Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法

  • RSA加密方式

公钥加密( E, N ):密文 = 明文^E % N

私钥解密( D, N ):明文 = 密文^D % N

  • 数学基础

在钥匙产生的过程之前,先来说说这里涉及的数学知识。

互质:我们知道质数就是只能被自己和1整除的数,互质的两个数就是公约数只有1的数。所以根据定义就可以知道其特性:

  1. 两个质数一定互质;
  2. 两个数中,其中是质数,另一个数比这个质数小或者不是这个质数的倍数,这两个质数也是互质的。

……还有很多的例子。只要知道这两条就够用了。

欧拉函数φ(x):小于x的正整数中与x互质的数的数目。(下面图中:p1, p2……pn为x的所有质因数,x是不为0的整数

它有特性如下:

  1. 如果n是质数,则φ(n) = n - 1
  2. 如果m和n是互质的,则φ(mn)  = φ(m) * φ(n)
  3. 如果m和都是质数,则φ(mn)  = (m - 1) * (n - 1) 

欧拉定理:如果m与n互质,则m^φ(n) % n = 1

逆元:又叫模反元素,如果m与n互质,则可以找到一个正整数d,使得m * d % n = 1

  • 密钥产生过程

有了上面的数学基础,就可以得到下面的钥匙产生过程。

  1. 选择两个不相等的质数p,q,得 N = pq

  2. 计算φ(N),选择一个1 < E < φ(N),且E与φ(n)互质,得公钥(E, N)

  3. 计算E与φ(N)的逆元 D,得私钥(D, N),D最小的值可以取φ(N)/E

下面举一个例子。

首先是两个不相等的质数,取小一点方便计算,取p = 3,q = 5;N = 15,因为3和5互质,所以φ(15) = φ(3) * φ(5)  =  (3 - 1 ) * (5 - 1) = 8。现在选择一个E,与φ(15)互质,即与8互质,选择E = 7,得公钥(7, 15)。计算一个逆元D,因为(D * 7 )%15 = 1,所以得D = 13,就得到私钥(13, 15)

到这里公钥与私钥就产生了,如果对公钥加密,私钥解密的过程有兴趣的话,可以进入 这里 看看证明的过程。

上面计算逆元的时候,是暴力搜索的方式来计算的。

有了上面的钥匙,就可以对信息进行加密了,因为加密与解密都是模幂运算,所以需要考虑溢出和效率的的问题,在上面暴力搜索逆元的时候,就是效率非常低的算法。这个时候就需要用到快速幂取模运算了。

  • 快速幂取模运算

  • 同余定理
(a +/-/* b) % c = ((a % c +/-/* b % c)) % c 


a^b % c = (a * a * a……a)%c 
 = ((a%c)*(a%c)*(a%c)*……*(a%c))%c
 = (a % c)^b % c
  • 模幂运算

分解模幂运算:

对于一个正整数b,其二进制的展开:

        b = b₀*2⁰ + b₁ * 2¹ + …… + bn * 2^n

于是    a^b = a^(b₀*2⁰ + b₁ * 2¹ + …… + bn * 2^n)

又等于  a^b = a^(b₀*2⁰) * a^(b₁ * 2¹) * …… * a^(bn * 2^n)

由于不是b的每个二进制都是 1 ,因此去掉二进制位是 0 的位(因为 a^0 = 1),就变成了
    
        a^b = a^(bi * 2^i) * …… * a^(bn * 2^n),这里的bi~bn都是 1

于是    a^b = a^(2^i) * …… * a^(2^n)

这时    a^b%c = (a^(2^i) * …… * a^(2^n))%c

由同余定理,得

        a^b%c = (a^(2^i)%c * …… * a^(2^n)%c)%c

令 Ai = (a^(2^i))%c,于是 (a^b)%c = (Ai * …… * An)%c;

即就是 b 的二进制位为 1 时,连乘 Ai,最后余 c。

而我们知道,对于二进制老说,当前位的值是前一个位的2倍

0001 = 1
0010 = 2
0100 = 4
1000 = 8

因此 Ai = A(i - 1) * A(i - 1)

于是对于消息的解密与解密就可以运算了。现在可以试试简单的消息加密了。一个初始版本的RSA就简单的实现了。具体的源代码见我的github。初始版本的RSA实现。

  • 演示一下消息加密/解密

  • 进阶之路

如果看了上面的初始版本的RSA。就会发现其很大的缺陷就是,这素数也太小了。由于公钥是公开的,也就是e和n是公开的,现在就是通过e和n来求d,如果被破解了,也就不安全了,所以RSA安全性的高低就取决于通过ne推导出d的难易程,其等价于分解两个大素数之积。为什么这么说,因为

从上面产生密钥的步骤来倒推,就可以发现

(ed) mod φ(n) =1。只有知道eφ(n),才能算出d,e和n是已知的。

φ(n)=(p-1)(q-1)。只有知道pq,才能算出φ(n)

n=pq。只有将n因数分解,因此求出了p和q,d就不保了。

因此需要更大位数的素数,于是我采用了boost大数库里面的大数。boost::int1024_t。

有了大数就需要对素数的产生来进行优化。

  • 素数的产生

首先先产生一个大的随机数,在进行是否是素数的判断,如果不是,就继续随机产生,因此这里也是一个比较浪费时间的地方,但是钥匙我们只生成一次,所以也就无所谓了。

br::mt19937 ran(time(nullptr));
br::uniform_int_distribution<bm::int1024_t> dist(1, bm::int1024_t(1) << 50);
//这里我采用最大的随机数是1 << 50位,也就是2^50。

用boost大数库里面的素性检测算法。

br::mt11213b gen(time(nullptr));
if (miller_rabin_test(prime, 25, gen))
{
	if (miller_rabin_test((prime - 1) / 2, 25, gen))
	{
		return true;
	}
}
return false;
  • 逆元的产生

在前面逆元的产生是通过暴力搜索的方式来产生的,因此如果是位数很大的数来说,就非常浪费时间。在前面求公钥的时候,就已经用到了欧几里得算法。现在通过扩展的欧几里得算法来进行优化求逆元的效率。

当前的欧几里得算法
int gcd(int a, int b) 
{
    if(b == 0)
        return a;
    return gcd(b, a%b);
}

扩展算法:

对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数对 x,y ,使得 gcd(a,b)=ax+by。

因此在求出(a,b)的最大公约数gcd,便求出其中的一组解(x,y),满足等式ax + by = gcd。从上面的前面欧几里得算法可知当b=0时,gcd即可求出,所以可以在求出gcd的同时也求出(x,y)

对于等式,ax + by = gcd, 当b = 0时, 等式变成了ax = gcd,此时我们返回a的值,a既是所求的最大公约 gcd,故此时x = 1, y可以是任意值, 所以,简单的一组解即为(1, 0)

在算逆元的时候:ab%n = 1 ab - 1n的倍数, 即 ab + kn = 1, 这里放入加密算法中对应的即为: ed + kφ(n) = 1,其中e为加密密钥,d为解密密钥,eφ(n)互质,他们的最大公约数即为1,所以这里的解密密dk就相当于等式的一组解,此解可以用扩展的欧几里得算法求解。

namespace bm = boost::multiprecision;
bm::int1024_t RSA::ProduceGcd(bm::int1024_t ekey, bm::int1024_t orla, bm::int1024_t& x, bm::int1024_t& y)
{
	//扩展的欧几里得算法
	if (orla == 0)
	{
		x = 1;
		y = 0;
		return ekey;
	}
	bm::int1024_t ret = ProduceGcd(orla, ekey%orla, x, y);
	bm::int1024_t tmpx = x;
	bm::int1024_t tmpy = y;
	x = tmpy;
	y = tmpx - (ekey / orla)*tmpy;
	return ret;
}
bm::int1024_t RSA::producedkey(bm::int1024_t ekey, bm::int1024_t orla)
{
	//计算e与f(n)的逆元 D,得密钥(D, n)

	//由扩展的欧几里得算法

	bm::int1024_t dkey = 1;
	bm::int1024_t Y = 3;
	bm::int1024_t tmp = ProduceGcd(ekey, orla, dkey, Y);
	dkey = (dkey%orla + orla) % orla;
	return dkey;
}

最后完整的RSA就诞生了,因为素数的产生过程比较慢,而且对于钥匙只产生一次,因此,第一次启动程序时我将其写入到文件当中,后面就只读取文件的素数就好,如果需要更换,删除配置文件就好。

完整源代码见我的github。最终版本的RSA算法实现

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值