面试准备+复习分享:
为了应付面试也刷了很多的面试题与资料,现在就分享给有需要的读者朋友,资料我只截取出来一部分哦
φ(n),k为任意整数,d便是e的模反元素。同时也可得到,e的模反元素d并不是唯一的。
例如,e=3,φ
(
n
)
=
11
\varphi(n)=11
φ(n)=11,则d=4
±
\pm
±k·11。
至此,公钥,私钥便都已经得到。
6、将e、n公开作为公钥进行加密
假设明文为M,密文为C,则加密过程为
M
e
m
o
d
n
=
C
M^e mod n =C
Memodn=C
7、将d,n作为私钥进行解密
C
d
m
o
d
n
=
M
C^d mod n =M
Cdmodn=M
二、RSA算法使用c++语言实现时遇到的问题以及解决方案
1.问题1:模反元素的求解
在第5步中,我们需要计算出e对于
φ
(
n
)
\varphi(n)
φ(n)的模反元素d。
如果使用暴力法求解,那么时间复杂度势必为O(n),这对于一个很大的整数来说,势必需要很长的时间去运行,效率低下,因此需要用一种更快的算法去优化该过程。
解决办法:在第四步中,我们随机产生一个整数e, e的范围是1 < e < φ(n), 并且e与 φ(n) 互质.互质的两个数的最大公约数为1,我们便可以用欧几里得算法去判断是否互质。
欧几里得算法可以用公式表示为gcd(a,b)=gcd(b,a mod b),利用辗转相除的思路。设F(x,y)表示为x,y的最大公约数,取k=x/y,b=x%y,那么x=ky+b,如果一个数能够整除x和y,那么一定可以整除y和b,也就是说,能够整除y和b的数,一定能够整除x和y,所以x和y的公约数和y和b的公约数相同,其最大公约数也相同,则有F(x,y)=F(y,x%y)(x>=y>0),这样就把求两个数的最大公约数转化为求两个更小的数的公约数,直到其中一个数为0,那么另一个数就是两者的最大公约数。
举个例子:
F(42,30)=F(30,12)=F(12,6)=F(6,0)=6
但对于以上过过程,我们如何用代码去实现,我们先从公约数的特点入手。
设x=
k
1
x
1
k_1x_1
k1x1,y=
k
1
y
1
k_1y_1
k1y1,则F(x,y)=
k
1
k_1
k1 F(x1,y1)。
设x=px
1
x_1
x1,(p为素数且y%p!=0)则F(x,y)=F(x1,y)。
现在取p=2。
若x,y均为偶数,则F(x,y)=2F(x/2,y/2)。
若x为偶数,y为奇数则F(x,y)=F(x/2,y)。
若x为奇数,y为偶数则F(x,y)=F(x,y/2)。
若x和y同时为奇数,F(x,y)=F(y,x-y)。(两个奇数相减后必定会出现一个偶数)
这种做法的算法复杂度为O(log2(max(x,y)))。
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int isoushu(long x)
{
if(x%2==0) return 1;
else return 0;
}
int fun(long long a,long long b)
{ if(b>a) return fun(b,a);
else if(a==0)return b;
else
{ if(isoushu(a))
if(isoushu(b))
{
return (fun(a>>1,b>>1)<<1);
}
else return fun(a>>1,b);
else
{ if(isoushu(b))
return fun(a,b>>1);
else return (a-b,b);
}
}
}
main()
{
long long a,b;
cin>>a>>b;
cout<<fun(a,b);
}
问题2:大数的幂运算
- 在计算机计算大数的指数的过程中, 计算的数字不断增大, 非常的占用我们的计算资源
- 我们计算的中间过程数字大的恐怖, 我们现有的计算机是没有办法记录这么长的数据的, 会导致溢出.
解决方案:在程序设计过程中,经常要去求一些大数对于某个数的余数,为了得到更快、计算范围更大的算法,产生了快速幂取模算法
从一个最简单的例子入手:求a^b % c = ?
int ans=1;
for(int i=1;i<=b;i++)
{
ans=ans\*a;
}
ans=ans%c;
该程序实现存在很大的问题,首先算法时间度为O(n),其次如果a和b过大,很容易就会溢出。
如何进行改进?这里就要用到中学时学到的同余定理:积的取余等于取余的积的取余。
a
b
a^b
ab% c = (a * a * a … a) % c
= ((a % c) * (a % c) * (a % c) … (a % c)) % c
= (a % c)b
^b
b% c
此时我们可以从上述公式中想到既然某个因子取余之后相乘再取余余数保持不变,那么可以通过改良程序得到以下版本。
int ans=1;
a=a%c;//加上这一句
for(int i=1;i<=b;i++)
{
ans=(ans\*a)%c;//取余
}
ans=ans%c;
但是这个算法在时间复杂度上没有改进,仍为O(b),不过已经好很多的,但是在c过大的条件下,还是很有可能超时,所以,我们需要用到快速幂算法来进行改进。
关于快速幂的算法的介绍,我么从推导其公式入手。
首先我们将十进制的指数b转换为2进制的形式
其中
a
n
a_n
an代表对应对应第n位的二进制值,非0即1。那么由二进制转换为十进制的公式我们可以得到
化简上述公式可得
则
a
b
a^b
abmod c可重写为
此处的
a
i
a_i
ai要么为0,要么为1,如果
a
i
a_i
ai为0,那么这一项值就为1。所以可以再次将上述式子化简为
其中
k
i
k_i
ki表示
a
i
a_i
ai不为0的项。
同时发现,对于每一项的计算时,计算后一项的结果时用前一项的结果的平方取余。用公式表示为
形如上式的公式推导,我们将其用代码实现。
int ans=1;
a=a%c;
while(b>0)
{
if(b%2==1)
ans=(ans\*a)%c;
b=b/2;
a=(a\*a)%c;
}
此时,当b=0时,所有的因子都已经相乘,算法结束。于是便可以在O(log b)的时间内完成了。所以在进行模幂运算时, 运算的复杂度已经和指数b的大小没有直接关系了, 有直接关系的是b的二进制bit位数。
问题3:大数的加/减/乘/除/模运算
在RSA算法中的数字极大, 是我们现有计算机无法直接计算的, 所以需要我们手动实现大数的加减乘除. 所以在实现中,直接调用boost库中的大数运算库cppp_int。
boost中的cpp_int库的介绍文档
https://www.boost.org/doc/libs/1_58_0/libs/multiprecision/doc/html/boost_multiprecision/tut/ints/cpp_int.html
项目使用的是boost_1_83_0版本的大数库,下载链接https://www.boost.org/users/download/
在VS中还需要将解压好的boost库路径添加到附加目录, 步骤如下
问题4:大数产生随机数
我们平时用的随机数函数是 srand()设置随机数种子, 一般传入time(0), 用 rand()获取随机数, 随机数 = rand % num; 随机数范围是 [0, num). 在不同平台的不同编译器下rand()函数所能获取的随机数的范围不同, 但由于目前CPU位数的限制, 顶多也就是个64位的数字, 所以我们还需要能产生大随机数的方法.
解决方案:在boost库的random库中 有着大数随机数的获取方法,在调用时包含头文件#include<boost/multiprecision/random.hpp>
问题5:大数产生质数
大数的素性检测有专门的算法, 比如fermat检测, Miller-Rabin等算法. 在boost库中的实现了Miller-Rabin素性检测算法,使用时添加头文件#include <boost/multiprecision/miller_rabin.hpp>
三、RSA加密算法代码实现
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;
# Ending
**Tip:由于文章篇幅有限制,下面还有20个关于MySQL的问题,我都复盘整理成一份pdf文档了,后面的内容我就把剩下的问题的目录展示给大家看一下**
**如果觉得有帮助不妨【转发+点赞+关注】支持我,后续会为大家带来更多的技术类文章以及学习类文章!**(阿里对MySQL底层实现以及索引实现问的很多)
![](https://img-blog.csdnimg.cn/img_convert/90e01f86238931f842501012841982dd.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/5e7a9a4f3ecc69476bc5b3d6eb39db0b.webp?x-oss-process=image/format,png)
吃透后这份pdf,你同样可以跟面试官侃侃而谈MySQL。其实像阿里p7岗位的需求也没那么难(但也不简单),扎实的Java基础+无短板知识面+对某几个开源技术有深度学习+阅读过源码+算法刷题,这一套下来p7岗差不多没什么问题,还是希望大家都能拿到高薪offer吧。
> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**
**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**
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;
# Ending
**Tip:由于文章篇幅有限制,下面还有20个关于MySQL的问题,我都复盘整理成一份pdf文档了,后面的内容我就把剩下的问题的目录展示给大家看一下**
**如果觉得有帮助不妨【转发+点赞+关注】支持我,后续会为大家带来更多的技术类文章以及学习类文章!**(阿里对MySQL底层实现以及索引实现问的很多)
[外链图片转存中...(img-jjqWlX2X-1715323277737)]
[外链图片转存中...(img-05tiEtEa-1715323277738)]
吃透后这份pdf,你同样可以跟面试官侃侃而谈MySQL。其实像阿里p7岗位的需求也没那么难(但也不简单),扎实的Java基础+无短板知识面+对某几个开源技术有深度学习+阅读过源码+算法刷题,这一套下来p7岗差不多没什么问题,还是希望大家都能拿到高薪offer吧。
> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**
**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**