项目 RSA文件加密工具

目录

传统的加密技术

非对称加密

RSA加密算法

RSA算法原理

RSA的E, D, N产生过程

RSA加密的安全性

RSA算法实现中的问题以及解决办法

问题1 : 模反元素的求解

问题2 : 大数的幂运算 

问题3 : 大数的加/减/乘/除/模运算

问题4 : 大数产生随机数

问题5 : 大数产生素(质)数

RSA加密算法代码实现


传统的加密技术

在古代中国较 "流行" 使用淀粉水在纸上写字,遇碘水中使字呈现蓝色显现出来, 利用化学反应中的络合反应.

以及应用最为广泛的 对称加密, 什么是对称加密, 打个比方, 我想给我一个在远方的朋友送一个价值连城的宝贝, 我把这个宝贝放在保险箱中, 我设置了一个密码, 我自己知道, 我再偷偷告诉朋友, 在保险箱运送过程中, 宝贝是安全的, 没有人能打开它, 当朋友收到保险箱后就可以按照密码打开. 即用一种方法加密, 用同一种方法解密, 即为对称加密. 像我们在谍战剧中看到的情报人员发出加密的电报, 在另一方收到后需要根据 "密码本" 来破译, 这就是典型的对称加密, 影视中有多少人为了保护这个 "密码本" 而失去生命, 情报的加密与破译也可能是决定一场战争走向的重要因素. 

对称加密的安全性,  首先取决于加密方法的保密性, 其次才取决于加密方法的破译难度

也就是说, 发送方加密的信息, 接收方要能解密, 就要知道加密方法. 这个加密方法的传输就成了很大的问题, 比如我国抗日战争以及解放战争中情报员运送的 "密码本"的过程, 就是传输解密方法的过程. 这个过程就会产生巨大的安全隐患.  其次就是加密方法的问题, 如今计算机技术发展迅速, 一般的加密方法在大量计算下可能都会被破译, 所以, 在当今网络通信安全需求如此巨大的时代, 单凭对称加密已经远不能满足人们的需求了.


非对称加密

非对称加密算法需要两个密钥:公开密钥 (publickey:简称公钥) 和 私有密钥 (privatekey:简称私钥). 公钥与私钥是一 一对应的,  如果用公钥对数据进行加密, 只有用对应的私钥才能解密.  因为加密和解密使用的是两个不同的密钥, 所以这种算法叫作非对称加密算法.

非对称加密算法实现机密信息交换的基本过程是 : 甲方生成一对密钥与公钥, 并将公钥公开, 需要向甲方发送信息的其他角色(乙方, 丙方等) 使用甲方公开的公钥对机密信息进行加密后再发送给甲方. 甲方再用自己私钥对加密后的信息进行解密.

甲方想要回复乙方时正好相反, 使用乙方公开的公钥对数据进行加密,同理,乙方使用自己的私钥来进行解密.

非对称加密的特点 : 算法强度复杂, 安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快. 非对称加密的保密性比较好,它消除了对称加密中用户交换密钥的需要 .


RSA加密算法

RSA是1977年由罗纳德·李维斯特(Ron Rivest), 阿迪·萨莫尔(Adi Shamir)和 伦纳德·阿德曼(Leonard Adleman)一起提出的. 当时他们三人都在麻省理工学院工作. RSA就是他们三人姓氏开头字母拼在一起组成的.

算法可靠性(安全性) : 极大整数做因数分解(公认的数学难题) 的难度决定了RSA算法的可靠性.

为什么说RSA算法的可靠性是基于极大整数的因数分解难度, 我们就得先了解RSA算法原理.

RSA算法原理

在RSA加密算法中, 会产生三个关键数据, 私钥(D), 公钥(E) 和 公共模数(N). 在加密信息传输流程中, 发送方需要有(E, N)来进行数据加密, 接收方需要有(D, N)来进行数据解密.

  RSA加密公式 :                                                                       RSA解密公式 : 

                                                        

解释 : 加密公式是密文等于明文的E次方模上一个N.  解密公式同理

那么问题来了, 怎么得到一组D, E, N 呢?

最容易获得是N值, 随机选择两个不相等的质数p 和 q, N = p*q  (p和q这两个质数越大, 就越难破解)

接下来是D和E的值, 在这之前, 我们先来了解一些概念

质数相关概念 : 

  • 两个质数一定是互质数
  • 如果两个数之中, 较大的那个数是质数, 则两者构成互质关系, 比如97和 [1, 96] 的96个数全是互质关系
  • 互质的两个数最大公约数为1

欧拉函数 :

  • 概念 : 欧拉函数的值是小于x的正整数中与x互质的数目
  • 通式 :  
  • 欧拉函数是积性函数, 若m, n互质, 则有φ(mn) = φ(m)φ(n)
  • 由互质数的概念可知, n为质数时, φ(n) = n - 1, 若m, n都为质数时, φ(mn) = (m - 1)(n - 1)

欧拉定理 :

  • 概念 : 如果两个正整数a和n互质,则n的欧拉函数 φ(n) 可以让下面的等式成立
  • "≡" 是数论中表示同余的符号, 表示等式两边求余的结果相同, 上面公式的意思是a的φ(n)次方除以 n 的余数等于 1除以n的余数,  1除以n的余数是1, 也就是说a的φ(n)次方除以 n 的余数等于1. 等价于下面的公式

模反元素(逆元)

  • 概念 : 根据欧拉定理, 如果两个正整数a 和 n互质, 那么一定可以找到整数b, 使得ab - 1可以被 n 整除, 或者说 ab 除以n的余数是1, ab mod n = 1,  此时, b就叫做a的 "模反元素", 或者成为模的逆元.

RSA的E, D, N产生过程

  • 1. 随机选择选择两个不相等的质数p和q
  • 2. 计算p和q的乘积 : n = pq. 得到公共模数 N = n
  • 3. 计算n欧拉函数的值,  φ(n).
  • 4. 随机产生一个整数e, e的范围是1 < e  φ(n), 并且e与 φ(n) 互质.
  • 5. 计算e对于 φ(n)的模反元素d, 使得 de ≡ 1 mod φ(n), 即 (de) mod φ(n) = 1
  • 6. 公钥(e, n), 私钥(d, n)

来举个栗子 :

  • 选择p = 3, q = 11
  • n = pq = 33
  • 由于p和q都为质数, φ(n) = (p -1)(q -1) = 20
  • 随机选择 e = 3 (1 < e < 20, 并且 e与20互质)
  • (de) mod φ(n) = (3d) mod 20 = 1, 可得其中一个解 d = 7
  • 公钥(3, 33), 私钥(7, 33)
  • 加密 : 对明文数字 4 加密, 根据加密公式, 密文 = (4^3) mod 33 = 64 mod 33 = 31
  • 解密 : 根据解密公式 , 明文 = (31^7) mod 33 = 27512614111 mod 33 = 4

RSA加密的安全性

RSA密钥产生过程中共生成6个值p(极大质数), q(极大质数q!=p), n(公共模数), φ(n)(欧拉函数值), e(私钥), d(公钥). 除过n, e之外, 其它值都是不公开的, p和q一般会销毁, d不能泄漏.

RSA安全性的高低就取决于通过n和e推导出d的难易程度, 由密钥产生的第五步可以知道 :

  • (ed) mod φ(n) = 1. 只有直到e 和 φ(n), 才能算出d
  • φ(n) = (p - 1)(q - 1). 也就是直到p和q才能算出φ(n)
  • n=pq. 只有将 n 因数分解, 才能算出p和q, 到此, RSA算法的可靠性就由极大整数的因数分解难度决定

破解RSA到目前没有任何有效方法, 只能用穷举法, 在计算机中进行大量的运算, 随着计算机技术的不断发展, RSA中因数分解的问题可能会解决, 但就像下面例子中的破解一样, 破解一个512bit的大数就需要五个月, 已经失去了时效性, 因此只要密钥长度足够长, RSA机密的信息实际是不能被破解的.

1. 针对RSA最流行的攻击一般是基于大数因数分解. 1999年,RSA-155 (512 bits)被成功分解, 花了五个月时间(约8000 MIPS年)和224 CPU hours在一台有3.2G中央内存的Cray C916计算机上完成.

2. 2009年12月12日, 编号为RSA-768(768 bits, 232 digits)数也被成功分解. 这一事件威胁了现通行的1024-bit密钥的安全性, 普遍认为用户应尽快升级到2048-bit或以上.

3. 量子计算里的秀尔算法能使穷举的效率大大的提高. 由于RSA算法是基于大数分解(无法抵抗穷举攻击),因此在未来量子计算能对RSA算法构成较大的威胁. 一个拥有N量子比特的量子计算机,每次可进行2^N次运算,理论上讲,密钥为1024位长的RSA算法,用一台512量子比特位的量子计算机在1秒内即可破解.


RSA算法实现中的问题以及解决办法

RSA算法实现中的问题都是围绕大数运算而产生的, 由于我们的计算机CPU位数的限制, 大数的运算就产生了困难, 具体问题如下 : 

  • 问题1 : 模反元素的求解
  • 问题2 : 大数的幂运算
  • 问题3 : 大数的加/减/乘/除/模运算
  • 问题4 : 大数产生随机数
  • 问题5 : 大数产生素(质)数

问题1 : 模反元素的求解

第五步 : (de) mod φ(n) = 1 式子中求d的值.

使用暴力搜索, 求模反元素d的时间复杂度为O(n),这对于大数来说, 效率方面是无法忍受的.所以需要更快速的算法.

在第四步 : 随机产生一个整数e, e的范围是1 < e  φ(n), 并且e与 φ(n) 互质.  利用互质的两个数最大公约数为1, 利用算法判断互质

欧几里得算法(辗转相除法) : gcd(a, b) = gcd(b, a mod b)

这是一个我们在初高中就学过的计算两个数的最大公约数的方法, 代码实现有递归和非递归

#include<iostream>

int gcd1(int a, int b) {
	return b ? gcd1(b, a % b) : a;
}
int gcd2(int a, int b) {
	int tmp;
	while (b) {
		tmp = a;
		a = b;
		b = tmp % b;
	}
	return a;
}
int main() {
	std::cout << gcd1(10, 11) << std::endl;
	std::cout << gcd2(10, 11) << std::endl;
	return 0;
}

但在第五步(de) mod φ(n) = 1 中求 d, 如果用暴力法(穷举法)的话太慢, 所以引入下面方法

欧几里得定理 : 如果 a 和 b 的最大公约数是gcd时, 那么就一定有 ax + by = gcd (x,y,gcd 属于整数, 可正可负)

例如 : 15 和 10的最大公约数为5, 则有15 + (-1)*10 = 5 ;   98 和 24 的最大公约数为2, 则有98 + (-4)*24 = 2 

扩展的欧几里得算法 : 用于求解形如 ax +by = c (a,b,c 属于整数, 可正可负)  的方程

现在再回过头来看 (de) mod φ(n) = 1, 转化为 (de - 1) mod φ(n) = 0, 即de -1 是 φ(n) 的倍数, 即有 kφ(n) = de - 1  ==>  kφ(n) + ed =  1 (k < 0)  此时方程就形如 ax + by = c了. 所以求 d 的值我们用扩展的欧几里得算法, 具体如下:

void exgcd(int a, int b, int* x, int* y) {
	if (b == 0) {
		*x = 1;
		*y = 0;
		return;
	}
	exgcd(b, a % b, x, y);
	int tmp = *x;
	*x = *y;
	*y = tmp - a / b * (*y);
}

问题2 : 大数的幂运算 

具体问题

  • 1. 在计算机计算大数的指数的过程中, 计算的数字不断增大, 非常的占用我们的计算资源
  • 2. 我们计算的中间过程数字大的恐怖, 我们现有的计算机是没有办法记录这么长的数据的, 会导致溢出.

解决办法 :快速幂取模运算

首先了解一下同余定理, 公式如下

(a + b) % c = ((a % c) + (b % c)) % c
(a - b) % c = ((a % c) - (b % c)) % c
(a * b) % c = ((a % c) * (b % c)) % c
(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 

模幂运算

对于 a^b % c, 其中b的展开式如下 : 

 

公式中的b0, b1...bn对应的是b的二进制第1位, 第2位....第n位, 二进制的值只有两种可能, 0或1, 所以我们的a^b运算就可以拆解成

那么我们去除掉其中bi 为0的二进制位(即a^0 = 1), 保留bi = 1之后得到下面式子

   

                                  

我们再用上面的同余定理, a^b % c 就可以转换为下面式子

  

如果我们令

        

其中 : 

               

我们发现A(n - 1) 和 An 之间 %c 之前的数是平方关系, 继续变形

                   

                   

根据同余定理中 (a * b) % c = ((a % c) * (b % c)) % c进行变形

                

这样模幂运算的最终推导式就推导出来了, 极大地简化了大数的模幂运算, 再回到计算最终结果的式子上

可以得到, 计算在最终结果需要直到Ai 到An 的值, 而又有 An = (A(n - 1) * A(n - 1)) % c, 也就是说最终我们在进行模幂运算时, 我们运算的复杂度已经和指数b的大小没有直接关系了, 有直接关系的是b的二进制bit位数


问题3 : 大数的加/减/乘/除/模运算

在RSA算法中的数字极大, 是我们现有计算机无法直接计算的, 所以需要我们手动实现大数的加减乘除. 

先自己手动实现一个带符号的加减乘除模运算

代码戳链接 : GitHub链接( ̄︶ ̄)↗ https://github.com/yyaihh/File-encryption-tools/tree/master/大数

                        码云链接( ̄︶ ̄)↗  https://gitee.com/yyaihh/file-encryption-tools/tree/master/大数

由于自己实现的一般没有大佬封装的库中的效率高, 所以我们在项目中直接用C++现有的库, boost库中的大数运算库cppp_int

这里我用的是boost_1_58_0版本的大数库,下载链接 https://nchc.dl.sourceforge.net/project/boost/boost/1.58.0/boost_1_58_0.7z

使用需添加头文件#include<boost/multiprecision/cpp_int.hpp>, 本次开发编译器是VS2017, boost库下载解压好之后, 在VS中还需要将解压好的boost库路径添加到附加目录, 步骤如下

 boost中的cpp_int库的介绍文档https://www.boost.org/doc/libs/1_58_0/libs/multiprecision/doc/html/boost_multiprecision/tut/ints/cpp_int.html

下面简单介绍一下cpp_int库的用法

cpp_int        任意位的大数

int128_t;     有符号固定128位大数
int256_t;     有符号固定256位大数
int512_t;     有符号固定512位大数
int1024_t;    有符号固定1024位大数

uint128_t;     无符号固定128位大数
uint256_t;     无符号固定256位大数
uint512_t;     无符号固定512位大数
uint1024_t;    无符号固定1024位大数

它们的使用, 也就是加减乘除模等运算与内置类型是一样的, 因为boost库中重载了+ - * / % & | 左移, 右移,前置++/--, 后置++/--, +=, -=, *=, /= 等运算符, 甚至连sqrt(), max(), min(), abs()等数学计算中常用函数也都重载了, 所以我们原来怎么用内置类型, 就怎么用cpp_int中的大数类型.

但值得注意的是, cpp_int库中的大数初始化需要了解一下, 如下 :

#include<iostream>
#include<string>
#include<boost/multiprecision/cpp_int.hpp>

namespace bm = boost::multiprecision;
void test() {
	char n1[] = "1234567345643123333333333333333456777777777777777777543234565434565432";
	const char* n2 = "6234563425656643425675434235654321345671324567543245734256765345677586";
	const char* n3 = "6234563425656643425675434235654321345671324567543245734256765345677586";
	string s = n1;
	s += n2;
	s += n3;
	bm::int1024_t bn1(n1);
	bm::int1024_t bn2(s);
	bm::cpp_int bn3 = bn1 * bn2;
	std::cout << bn1 << std::endl << std::endl;
	std::cout << bn2 << std::endl << std::endl;
	std::cout << bn3 << std::endl << std::endl;
}
int main() {
	test();
	return 0;
}


问题4 : 大数产生随机数

我们平时用的随机数函数是 srand()设置随机数种子, 一般传入time(0), 用 rand()获取随机数, 随机数 = rand % num; 随机数范围是 [0, num). 在不同平台的不同编译器下rand()函数所能获取的随机数的范围不同, 但由于目前CPU位数的限制, 顶多也就是个64位的数字, 所以我们还需要能产生大随机数的方法.

解决方法 : 还是在boost库的random库中 有着大数随机数的获取方法.
需要包含头文件#include<boost/multiprecision/random.hpp>

#include<iostream>
#include<boost/multiprecision/cpp_int.hpp>
#include<boost/multiprecision/random.hpp>
namespace br = boost::random;
namespace bm = boost::multiprecision;

oid test() {
	bm::int1024_t n;
	br::mt19937 gen((size_t)time(0));//随机数发生器
        //mt19937:一种随机数产生器
	br::uniform_int_distribution<bm::int1024_t> dist(bm::int1024_t(0), bm::int1024_t(1) << 128);
        //设置范围[0, 1<<128)
	for (int i = 0; i < 10; ++i) {
		std::cout << dist(gen) << std::endl;
	}
}
int main() {
	test();
	return 0;
}


问题5 : 大数产生素(质)数

大数的素性检测有专门的算法, 比如fermat检测, Miller-Rabin等算法. 在boost库中的实现了Miller-Rabin素性检测算法

需要添加头文件#include <boost/multiprecision/miller_rabin.hpp>

说明文档 : https://www.boost.org/doc/libs/1_58_0/libs/multiprecision/doc/html/boost_multiprecision/tut/primetest.html

#include<iostream>
#include<boost/multiprecision/cpp_int.hpp>
#include<boost/multiprecision/random.hpp>
#include<boost/multiprecision/miller_rabin.hpp>
namespace br = boost::random;
namespace bm = boost::multiprecision;

bm::int1024_t GetPrime() {
	bm::int1024_t res;
	br::mt19937 gen((size_t)time(0));
	br::uniform_int_distribution<bm::int1024_t> dist(bm::int1024_t(0), (bm::int1024_t(1) << 128));
	while (!isPrime(res = dist(gen)));
	return res;
}
bool isPrime(bm::int1024_t& num) {
	br::mt11213b gen((size_t)time(0));//要和产生随机数的发生器不一样
	if (miller_rabin_test(num, 25, gen)) {
		if (miller_rabin_test((num - 1) / 2, 25, gen)) {
			return true;
		}
	}
	return false;
}
int main() {
	std::cout << GetPrime();
	return 0;
}

template <class Backend, expression_template_option ExpressionTemplates, class Engine>
bool miller_rabin_test(const number<Backend, ExpressionTemplates>& num, unsigned trials, Engine& gen)

这个函数用于素性检测, 若返回 false, 则说明检测的数一定不是一个素数, 若返回true, 则说明有很大可能是素数, 返回true时不是素数的可能性是0.25^trials, 是一个非常小的可能性. 如果第一次返回结果为真, 我们再检测一次(n - 1) / 2是否为素数, 如果两次素性检测都返回true, 那么num是素数的可能性无限接近于1, 我们则认为num就是素数. trials值推荐是25.


RSA加密算法代码实现

源代码链接: GitHub链接( ̄︶ ̄)↗ https://github.com/yyaihh/File-encryption-tools.git

                        码云链接( ̄︶ ̄)↗ https://gitee.com/yyaihh/file-encryption-tools.git

rsa.h 

#pragma once
#include<iostream>
#include<boost/multiprecision/cpp_int.hpp>//大数库
#include<boost/multiprecision/random.hpp>//随机数库
#include<boost/multiprecision/miller_rabin.hpp>//素性检测
#include<boost/algorithm/string.hpp> //spilt()接口就在这个库中

namespace br = boost::random;
namespace bm = boost::multiprecision;

#define NUMBER 128 //临时缓冲区大小
#define SECRET_KEY_PATH "secret_key.txt" //存储N,D,E的文件名
#define SIZE 128 //控制随机数大小, 范围是 0 ~ 1 << SIZE

typedef struct {
	bm::int1024_t m_ekey;//公钥e
	bm::int1024_t m_dkey;//私钥d
	bm::int1024_t m_nkey;//公共模数n
}Key;

class RSA {
	Key m_key;
	bm::int1024_t GetPrime();
	bool isPrime(bm::int1024_t& num);
	bm::int1024_t getNkey(bm::int1024_t& prime1, bm::int1024_t& prime2);//获取公共模数n
	bm::int1024_t getOrla(bm::int1024_t& prime1, bm::int1024_t& prime2);//欧拉函数, 得到f(n)
	bm::int1024_t getEkey(bm::int1024_t& orla);//获取公钥
	bm::int1024_t getDkey(bm::int1024_t& ekey, bm::int1024_t& orla);//获取私钥
	void exGcd(bm::int1024_t a, bm::int1024_t b, bm::int1024_t* x, bm::int1024_t* y);//求模反元素
	bm::int1024_t getGcd(bm::int1024_t num1, bm::int1024_t num2);//最大公约数
	bm::int1024_t _encrypt(bm::int1024_t data, bm::int1024_t ekey, bm::int1024_t nkey);//加密,需要加密数据和公钥(e, n)
	bm::int1024_t _decrypt(bm::int1024_t data, bm::int1024_t dkey, bm::int1024_t nkey);//解密,需要要解密的数据和私钥(d, n)
	void getKeys();
public:
	RSA();
	bool encrypt(const std::string filename, const std::string outname);//加密
	bool decrypt(const std::string filename, const std::string outname);//解密
};

 rsa.cpp

#include<fstream>
#include<vector>
#include"rsa.h"
using namespace std;

RSA::RSA() {
	getKeys();
}
bm::int1024_t RSA::getNkey(bm::int1024_t& prime1, bm::int1024_t& prime2) {
	return prime1 * prime2;
}
bm::int1024_t RSA::getOrla(bm::int1024_t& prime1, bm::int1024_t& prime2) {//prime1和prime2必须互质
	return (prime1 - 1) * (prime2 - 1);
}
bm::int1024_t RSA::getEkey(bm::int1024_t& orla) {
	bm::int1024_t ekey;
	br::mt11213b gen((size_t)time(0));
	br::uniform_int_distribution<bm::int1024_t> dist(bm::int1024_t(0), (bm::int1024_t(orla)));
	do {
		ekey = dist(gen);
	} while (ekey < 2 || getGcd(ekey, orla) != 1);
	return ekey;
}
bm::int1024_t RSA::getDkey(bm::int1024_t& ekey, bm::int1024_t& orla) {
	bm::int1024_t x, y;
	exGcd(ekey, orla, &x, &y);
	return (x % orla + orla) % orla;//变换, 让解密密钥是一个比较小的数
}
bm::int1024_t RSA::getGcd(bm::int1024_t num1, bm::int1024_t num2) {
	bm::int1024_t num;
	while ((num = num1 % num2)) {
		num1 = num2;
		num2 = num;
	}
	return num2;
}
void RSA::exGcd(bm::int1024_t a, bm::int1024_t b, bm::int1024_t* x, bm::int1024_t* y) {
	if (b == 0) {
		*x = 1;
		*y = 0;
		return;
	}
	exGcd(b, a % b, x, y);
	bm::int1024_t tmp = *x;
	*x = *y;
	*y = tmp - a / b * (*y);
}
bm::int1024_t RSA::_encrypt(bm::int1024_t Ai, bm::int1024_t ekey, bm::int1024_t nkey) {
	//data^ekey % nkey
	//只和ekey的位数有关
	bm::int1024_t res = 1;
	for (; ekey; ekey >>= 1) {
		if (ekey & 1) {
			res = (res*Ai) % nkey;
		}
		Ai = (Ai*Ai) % nkey;
	}
	return res;
}
bm::int1024_t RSA::_decrypt(bm::int1024_t data, bm::int1024_t dkey, bm::int1024_t nkey) {
	return _encrypt(data, dkey, nkey);
}
bool RSA::isPrime(bm::int1024_t& num) {
	br::mt11213b gen((size_t)time(0));//要和产生随机数的发生器不一样
	if (miller_rabin_test(num, 25, gen)) {
		if (miller_rabin_test((num - 1) / 2, 25, gen)) {
			return true;
		}
	}
	return false;
}
bm::int1024_t RSA::GetPrime() {
	bm::int1024_t res;
	br::mt19937 gen((size_t)time(0));
	br::uniform_int_distribution<bm::int1024_t> dist(bm::int1024_t(0), (bm::int1024_t(1) << SIZE));
	while (!isPrime(res = dist(gen)));
	return res;
}
void RSA::getKeys() {
	FILE* fp;
	if ((fp = fopen(SECRET_KEY_PATH, "r")) == NULL) {
		cout << "生成密钥, 公钥中...\n";
		bm::int1024_t prime1, prime2 = GetPrime();
		while ((prime1 = GetPrime()) == prime2);
		m_key.m_nkey = getNkey(prime1, prime2);
		bm::int1024_t orla = getOrla(prime1, prime2);
		m_key.m_ekey = getEkey(orla);
		m_key.m_dkey = getDkey(m_key.m_ekey, orla);
		stringstream tmp;
		tmp << m_key.m_nkey << '\n' << m_key.m_ekey << '\n' << m_key.m_dkey << '\n';
		ofstream fout(SECRET_KEY_PATH, ofstream::binary);
		if (fout.is_open() == false) {
			perror("file open failed!");
			return;
		}
		fout.write(tmp.str().c_str(), tmp.str().size());
		if (fout.good() == false) {
			cout << "file " << SECRET_KEY_PATH << " read data failed!\n";
		}
		fout.close();
	}
	else {
		fseek(fp, 0L, SEEK_END);
		size_t fsize = ftell(fp);
		fclose(fp);
		ifstream fin(SECRET_KEY_PATH, ifstream::binary);
		if (fin.is_open() == false) {
			perror("file open failed!");
			return;
		}
		string buf;
		buf.resize(fsize);
		fin.read(&buf[0], fsize);
		if (fin.good() == false) {
			cout << "file " << SECRET_KEY_PATH << " read data failed!\n";
		}
		vector<string> secret_key;
		//split用于分割字符串
		boost::split(secret_key, buf, boost::is_any_of("\n"), boost::token_compress_on);
		m_key.m_nkey = bm::int1024_t(secret_key[0]);
		m_key.m_ekey = bm::int1024_t(secret_key[1]);
		m_key.m_dkey = bm::int1024_t(secret_key[2]);
		fin.close();
	}
}
bool RSA::encrypt(const string filename, const string outname) {
	ifstream fin(filename, ifstream::binary);
	ofstream fout(outname, ifstream::binary);
	if (!fin.is_open()) {
		perror("input file open failed!");
		return false;
	}
	char* buffer = new char[NUMBER];
	bm::int1024_t* bufferOut = new bm::int1024_t[NUMBER];
	while (!fin.eof()) {
		fin.read(buffer, NUMBER);
		streamsize ret = fin.gcount();
		for (streamsize i = 0; i < ret; ++i) {
			bufferOut[i] = _encrypt(buffer[i], m_key.m_ekey, m_key.m_nkey);
		}
		fout.write((char*)bufferOut, ret * sizeof(bm::int1024_t));
	}
	delete[] bufferOut;
	delete[] buffer;
	fin.close();
	fout.close();
	return true;
}
bool RSA::decrypt(const string filename, const string outname) {
	ifstream fin(filename, ifstream::binary);
	ofstream fout(outname, ifstream::binary);
	if (!fin.is_open()) {
		perror("file open failed");
		return false;
	}
	bm::int1024_t* buffer = new bm::int1024_t[NUMBER];
	char* bufferOut = new char[NUMBER];
	while (!fin.eof()) {
		fin.read((char*)buffer, NUMBER * sizeof(bm::int1024_t));
		streamsize ret = fin.gcount() / sizeof(bm::int1024_t);
		for (streamsize i = 0; i < ret; ++i) {
			bufferOut[i] = (char)_decrypt(buffer[i], m_key.m_dkey, m_key.m_nkey);
		}
		fout.write(bufferOut, ret);
	}
	delete[] bufferOut;
	delete[] buffer;
	fin.close();
	fout.close();
	return true;
}

main.cpp

#include<iostream>
#include <windows.h>
#include"rsa.h"
using namespace std; 
void Test(const string filename, const string EncName, const string DecName) {
	RSA rsa;
	if (rsa.encrypt(filename, EncName)) {
		cout << "加密完成!\n";
		if (rsa.decrypt(EncName, DecName)) {
			cout << "解密完成!";
		}
	}
	cout << "3秒后程序退出!\n";
	Sleep(3000);//在Linux下为sleep(3), 需要添加头文件 unistd.h
}
int main() {
	string filename("明文.txt"), EncName("密文.txt"), DecName("解密后的文件.txt");
	Test(filename, EncName, DecName);
	return 0;
}

运行结果如下:

发布了232 篇原创文章 · 获赞 720 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/qq_41071068/article/details/105109442