RSA 加密算法在C++中的实现 面向初学者(附代码)
Posted EUREKA-X
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RSA 加密算法在C++中的实现 面向初学者(附代码)相关的知识,希望对你有一定的参考价值。
概述
博文的一,二部分为基础知识的铺垫。分别从密码学,数论两个方面为理解RSA算法做好了准备。第三部分是对RSA加密过程的具体介绍,主要涉及其密钥对(key-pair)的获取。前三个部分与编程实践无关,可以当作独立的关于RSA加密算法的介绍。第四部分开始介绍在编程层面实现RSA算法的基础知识,主要涉及一些算法,如拓展欧几里得算法,米勒-拉宾素性检验算法,是为C++中实现RSA加密所作的铺垫。第五部分阐述了面向初学者实现RSA算法的思路,以及其局限,可改善之处。第六部分为提供的参考代码。
一. RSA算法的密码学基础
密钥:将明文转换为密文,对于窃听者来说,密钥和明文等价。
对称加密(symmetric cryptograph),特征在于加密和解密使用同一个密钥。
非对称加密(asymmetric cryptography),也被称作公钥加密(public-key cryptography)。最主要的特征在于使用公钥加密,私钥解密。
下面我们通过一个例子,来简述非对称加密的过程,假设A,B两人进行公钥加密通信,则整个通信的过程,由信息接受者B启动(A为信息发出者)。
B首先通过一定算法生成包含公钥,私钥的密钥对(key-pair),然后将公钥发送给A,自己保留私钥,请求A利用这个公钥对信息进行加密。
A利用该公钥对信息进行加密后,将密文传送给B,B利用自己的私钥对密文进行解密。
值得注意的是:首先公钥是可公开的,因为光凭借公钥只能加密,而并不能解密,所以不用担心公钥传输过程中被窃听者截获,同理也不用担心密文被截获,因为唯一能够破解密文的密钥在信息接收者处。
RSA算法(Rivest-Shamir-Adleman 取自开发者首字母),正是一种公钥密码算法。
二. RSA算法的数论基础
(看之前需要理解同余符号的含义)
1.欧拉函数:
对于正整数n,不大于n,且与n互素的数的个数记为
2.欧拉定理:
(其弱化形式,即在n为素数时,变为费马小定理)
证明需要用到群论知识,与RSA算法关联不大,故在此不加赘述,可参考:
欧拉定理 证明及推论_有钱哥哥家的的博客-CSDN博客_欧拉定理证明
3.同余的一些基本性质:
/1:乘积同余:即两数乘积,与两数模n的余数的乘积,关于模n同余,证明可以通过将两数写作kn+q(q为余数)的形式,比较其乘积与q1,q2乘积在模n时的结果。
/2:幂运算同余:若,则,证明可以通过移项,由因式定理可知,其必有a-b这个因式
4.逆元:
若,则称a为b,关于模n的逆元
三.RSA算法介绍
我们用A来代表明文,B代表经过RSA算法加密后的密文。则可以用一个等式来阐明A,B间的关系:,且,即B为A的e次方后除以n的余数。其中(e,n)为公钥。
设(d,n)为私钥,则私钥满足的关系为
下面我们来看如何得到公钥和私钥组成的密钥对(需要用到二.介绍的数学知识)。
1.得到公钥:
选取两个充分大的素数p,q, 其乘积的值即为n,在得到n后,计算其欧拉函数的值,即在1到n-1中有多少数与n互素。
因为n包含两个质因子p,q,所以在1到n-1中包含p,q因子的数均与n不互素。
包含p因子的有p,2p,3p一直到p(q-1),同理含q的有q到q(p-1)。一共p+q-2个数
则在这n-1个数中与n互素的数一共有n-1-(p+q-2)=n-p-q+1,且n可以写作p*q,可以得到:
我们选取与互素的小于的数e,则(e,n)组成公钥。
2.得到私钥:
取e关于的逆元为d,则得到(d,n)私钥。
下面来证明为何(d,n)为私钥:
即证明:
两侧同时取e次幂可以得到
因为d为e的逆元,所以,将该等式带入到上式中,我们可以得到:
由欧拉定理可知,由同余的性质中的幂运算同余知,两侧同时取k次幂,可以得到:
,再由同余基本性质中的乘积同余,可知,此即为公钥的条件,于是我们发现在d取e 关于的逆元时,两者等价,即私钥条件成立。
上述生成的公钥与密钥组成的密钥对便可用于加密。
RSA算法的核心在于,对于一个大数的质因数分解是很困难的,一旦能够发现对于大数质因数的高效算法,RSA就能够被破译。
四.RSA在C++实现的算法基础
在利用c++实现RSA加密时,含需要一些补充的算法知识:
1.裴蜀定理(Bezout’s lemma):
一定存在整数x,y,使得线性方程组ax+by=gcd(a,b)成立。而ax+by=gcd(a,b)则称为裴蜀等式。
2.拓展欧几里得算法(extended Euclidean algorithm):
欧几里得算法(辗转相除法)常用于求算最大公约数(gcd),而拓展欧几里得算法则是在具备欧几里得算法的功能前提下,增加了求解裴蜀等式的功能。而在我们通过公钥(e,n)计算私钥(d,n)时,就需要用到拓展欧几里得算法。
因为,可以写作
又因为,故上式可化为,符合裴蜀等式。
d与-k分别为欲求的x,y,故可以使用拓展欧几里得算法来求解
关于拓展欧几里得算法的细节可以参考:扩展欧几里得算法详解__Warning_的博客-CSDN博客_扩展欧几里得
3.米勒-拉宾素性检验(Miller-Rabin prime test)
RSA加密的关键在于其最初生成的两个充分大的素数p,q,其大小决定了密码破译的难度。但是要随机生成两个大素数是比较困难的,所以RSA算法中大多都采用通过米勒-拉宾素性检验的伪素数来作为p,q。
米勒-拉宾素性检验是基于费马小定理,对给定的任意奇数进行检验,检验通过则代表其有概率为素数,在进行多次检验后,若都通过,则其为素数的概率会非常高,可以作为素数使用。
五.RSA算法在C++中的实现
基本思路:
1.随机素数p,q的获取:利用数组,生成一定范围内一定质数的数表,产生随机数i,j对应素数数组的下标,由此达到在数表中随机选取素数的功能。
2.密钥的获取:利用拓展欧几里得算法获取d的值。
3.加密过程:将输入字符的ASCII码值进行RSA加密,密文为一串数字,实现方法是用字符数组与整型数组间的值传递。
局限与改善:
1.没有使用米勒-拉宾素性检验来获取大素数,而是用素数数表产生的素数对,其构成的密钥空间小,一旦数表范围被获取,则密钥极有可能被破解。
2.加密文本的读入没有涉及文件的读写层面,需要依靠人为输入,较为不方便。
3.部分计算没有考虑在选取充分大的素数时可能产生的数据溢出问题。
六.C++代码
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
int exgcd(int a, int b,int *x,int *y) //拓展欧几里得算法
if(b==0)
*x=1;
*y=0;
return a;
int gcd=exgcd(b,a%b,x,y);
int temp=*x;
*x=*y;
*y=temp-a/b*(*y);
return gcd;
int isprime(int a) //素数判断
int i;
for(i=2;i<1+(a/2);i++)
if(a%i==0)return 1;
return 0;
void primegenerator(int prime[10]) //生成素数表
int i,j=0;
for(i=91;i<=1000;i++)
if(isprime(i)==0)
prime[j]=i;
j++;
if(j>9)break;
int main()
int prime[10];
primegenerator(prime);
int seed,p,q;
seed=time(0);
srand((unsigned int)seed); //生成在范围内的随机素数p,q
p=rand()%9;
do
q=rand()%9;
while(q==p);
int e,d,n,fi_n,r,nu,w1,w2;
int a;
cout<<"请选择加密/解密"<<endl;
cout<<"输入0代表加密"<<' '<<"输入1代表解密"<<endl;
cin>>a;
char minwen[1000];
int i,j,mi;
if(a==0)
n=prime[p]*prime[q];
fi_n=(prime[p]-1)*(prime[q]-1);
for(r=fi_n/2;n>=1;r--) //求得公钥
if(exgcd(r,fi_n,&w1,&w2)==1)
e=r;
break;
r=exgcd(e,fi_n,&d,&nu);
cout<<"请输入明文"<<endl;
scanf("%s",minwen);
int shuma_minwen[strlen(minwen)];
for(i=0;i<strlen(minwen);i++)
shuma_minwen[i]=minwen[i];
int shuma_miwen[strlen(minwen)]; //录入结束,开始加密
for(i=0;i<strlen(minwen);i++)
mi=shuma_minwen[i];
shuma_miwen[i]=1;
for(j=1;j<=e;j++)
shuma_miwen[i]=(shuma_miwen[i]*mi)%n;
cout<<"密文为"<<endl;
for(i=0;i<strlen(minwen);i++)
cout<<shuma_miwen[i]<<' '; //加密结束,输出密文,私钥
cout<<endl<<"密文长度为"<<i<<endl;
cout<<endl<<"解密私钥为"<<endl;
cout<<d<<' '<<n<<endl;
else if(a==1)
int shuma_jiemiwen[10000];
cout<<"请输入密文长度"<<endl;
int k;
cin>>k;
cout<<"请输入密文"<<endl; //录入密文
int t=0;
for(i=0;i<k;i++)
cin>>shuma_jiemiwen[i];
int sizel=k;
cout<<"请输入私钥(d,n) (分别输入d,n用空格隔开)"<<endl;
int d1,n1;
cin>>d1>>n1;
int ming;
int shuma_jieminwen[sizel]; //开始解密
for(i=0;i<sizel;i++)
ming=shuma_jiemiwen[i];
shuma_jieminwen[i]=1;
for(j=0;j<d1;j++)
shuma_jieminwen[i]=shuma_jieminwen[i]*ming%n1;
char jieminwen[sizel];
for(i=0;i<sizel;i++)
jieminwen[i]=shuma_jieminwen[i];
cout<<"明文为"<<endl; //输出明文
for(i=0;i<sizel;i++)
cout<<jieminwen[i];
return 0;
这里提供的代码部分参考于以下链接:
C语言实现简单的RSA加解密算法_♡Starry.的博客-CSDN博客_rsa加密算法代码c语言
但是做了一些改善:
1.由随机数与素数表的对应关系,给出素数对(p,q),无需人为输入
2.计算逆元d时,采用拓展欧几里得算法,时间复杂度更低
但仍有一些点可以继续改进:
1.没有使用米勒-拉宾素性检验来获取大素数,密码安全性较低
2.加密文本的读入没有涉及文件的读写层面,较为不方便。
此代码可以供初学者简单体会RSA加密的基本过程,以及主要算法,进一步优化有待探讨。
RSA密码算法C++实现
1. RSA非对称加密原理
网上一大把,这里推荐一篇比较好的博客
2. C++ 随机生成密钥版本
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include<string.h>
#include <math.h>
#include<algorithm>
using namespace std;
typedef long long ll;
// e是公钥
// d是私钥
ll e, d, n;
ll gcd(ll a, ll b) //求最大公约数
{
ll c = 0;
if(a<b) swap(a,b);
c = b;
do
{
b = c;
c = a%b;
a = b;
}
while (c != 0);
return b;
}
// 0不是 1是
ll isPrime(ll i) //判断i是否是素数
{
ll flag=0;
for(ll a=2; a<i; a++)
{
if(i%a==0)
{
flag=1;
break;
}
}
if(flag==1) return 0;
else return 1;
}
ll myPow(ll a, ll b, ll n) //求a^b mod n
{
ll y;
/*使用二进制平方乘法计算 pow(a,b) % n*/
y=1;
while(b != 0)
{
/*对于b中的每个1,累加y*/
if(b & 1)
y = (y*a) % n;
/*对于b中的每一位,计算a的平方*/
a = (a*a) % n;
/*准备b中的下一位*/
b = b>>1;
}
return y;
}
void extgcd(ll a,ll b,ll& d,ll& x,ll& y)
{
if(!b)
{
d=a;
x=1;
y=0;
}
else
{
extgcd(b,a%b,d,y,x);
y-=x*(a/b);
}
}
ll ModularInverse(ll a,ll b) //获取(1/a)mod b的结果
{
ll d,x,y;
extgcd(a,b,d,x,y);
return d==1?(x+b)%b:-1;
}
void KeyGeneration() //获取公钥密钥
{
ll p, q;
ll phi_n;
do
{
do
p = rand();
while (p % 2 == 0);
}
while (!isPrime(p)); // 得到素数 p
do
{
do
q = rand();
while (q % 2 == 0);
}
while (!isPrime(q)); // 得到素数q
n = p * q;
phi_n = (p - 1) * (q - 1);
do
e = rand() % (phi_n - 2) + 2; // 1 < e < phi_n
while (gcd(e, phi_n) != 1);
d = ModularInverse(e,phi_n);
}
// 一位一位地输出加密的结果
ll Encryption(ll value) //加密
{
ll cipher;
cipher = myPow(value, e, n);
cout<<cipher;
return cipher;
}
// 一位一位地输出解 密的结果
void Decryption(ll value) //解密
{
ll decipher;
decipher = myPow(value, d, n);
cout<<decipher;
}
int main()
{
/******
对6位的数字进行稳定加密
******/
ll num;
cout<<"请输入要加密的明文数字,ctrl+c结束"<<endl;
while(cin>>num){
ll de;
cout<<"输入的明文为"<<num<<endl;
KeyGeneration(); //获取公钥密钥
cout<<"加密密钥:"<<e<<endl;
cout<<"加密结果为:";
de = Encryption( num );
cout<<"\\n私钥为:"<<d;
// cout<<"de="<<de<<endl;
cout<<"\\n解密结果";
Decryption(de);
cout<<"\\n-------------"<<endl;
}
return 0;
}
3. 密钥固定e=13版本
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include<string.h>
#include <math.h>
#include<algorithm>
using namespace std;
typedef long long ll;
char map[30] = {
'a', 'b', 'c', 'd', 'e','f','g',
'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z'
};
//cp存密文, ep存明文
char cp[1000], ep[1000];
int lcp , lep ;
ll getNum(char a, char b){
int aa,bb;
for(int i=0; i<30; i++){
if(map[i]==a){
aa = i;
}
if(map[i]==b){
bb = i;
}
}
return aa*100+bb;
}
// flag = 1, 解密转换得到的是明文
// flag = 2 加密转换得到的是密文
void toChar(ll num, int flag = 1){
int a = num/100;
int b = num%100;
a %= 26;
b %= 26;
cout<<map[a]<<map[b]<<" "<<endl;
if(flag==1){
ep[lep++] = map[a];
ep[lep++] = map[b];
}
else if(flag==2){
cp[lcp++] = map[a];
cp[lcp++] = map[b];
}
}
// e是公钥
// d是私钥
ll e, d, n;
ll gcd(ll a, ll b) //求最大公约数
{
ll c = 0;
if(a<b) swap(a,b);
c = b;
do
{
b = c;
c = a%b;
a = b;
}
while (c != 0);
return b;
}
// 0不是 1是
ll isPrime(ll i) //判断i是否是素数
{
ll flag=0;
for(ll a=2; a<i; a++)
{
if(i%a==0)
{
flag=1;
break;
}
}
if(flag==1) return 0;
else return 1;
}
ll myPow(ll a, ll b, ll n) //求a^b mod n
{
ll y;
/*使用二进制平方乘法计算 pow(a,b) % n*/
y=1;
while(b != 0)
{
/*对于b中的每个1,累加y*/
if(b & 1)
y = (y*a) % n;
/*对于b中的每一位,计算a的平方*/
a = (a*a) % n;
/*准备b中的下一位*/
b = b>>1;
}
return y;
}
void extgcd(ll a,ll b,ll& d,ll& x,ll& y)
{
if(!b)
{
d=a;
x=1;
y=0;
}
else
{
extgcd(b,a%b,d,y,x);
y-=x*(a/b);
}
}
ll ModularInverse(ll a,ll b) //获取(1/a)mod b的结果
{
ll d,x,y;
extgcd(a,b,d,x,y);
return d==1?(x+b)%b:-1;
}
void KeyGeneration() //获取公钥密钥
{
ll p, q;
ll phi_n;
p = 43; q = 59, e=13;
n = p * q;
phi_n = (p - 1) * (q - 1);
d = ModularInverse(e,phi_n);
}
// 一位一位地输出加密的结果
ll Encryption(ll value) //加密
{
ll cipher;
cipher = myPow(value, e, n);
cout<<"加密得到的数字="<<cipher<<"\\t加密得到的字母=";
toChar(cipher, 2);
return cipher;
}
// 一位一位地输出解 密的结果
void Decryption(ll value) //解密
{
ll decipher;
decipher = myPow(value, d, n);
cout<<"解密得到的数字="<<decipher<<"\\t解密得到的字母=";
toChar(decipher, 1);
}
int main()
{
lcp = 0, lep = 0;
char st[] = "cybergreatwall";
KeyGeneration();
cout<<"e="<<e<<" d="<<d<<" n="<<n <<endl;
int len = strlen(st);
// cout<<st<<" "<<len<<endl;
for(int i=0; i<len; i+=2){
cout<<"加密的明文="<<st[i]<<st[i+1]<<endl;
ll num = getNum(st[i], st[i+1]);
cout<<"明文对应的数字"<<num<<endl;
ll de = Encryption( num );
Decryption(de);
}
cp[lcp] = '\\0';
ep[lep] = '\\0';
cout<<"加密的结果为"<<cp<<endl;
cout<<"解密的结果为:"<<ep<<endl;
return 0;
}
以上是关于RSA 加密算法在C++中的实现 面向初学者(附代码)的主要内容,如果未能解决你的问题,请参考以下文章