RSA非对称加密算法实现过程:
公私钥的生成,随机选定两个大素数p, q;
对于两个大素数,pq,计算公钥和私钥的公共模数 n = pq;
根据欧拉函数,求得φ(n) = (p-1)(q-1) ,选择一个小于 φ(n)的整数e;
对于正整数e, 使1 < e < φ(n) , 且e与φ(n)互质;
将p和q的记录销毁;
计算e关于φ(n)的模逆d, 满足de1 (mod φ(n) ), (‘’表示同余的概念).
{n,e}就是公钥,{n,d}就是私钥。秘钥的位数由n的值决定,与n所占用的二进制位宽相等。
#include<iostream>
#include<cstring>
using namespace std;
//判断是否为质数
bool isprime(int n){
if(n==1||n==0) return false;
for(int i=2;i*i<=n;i++)
if(n%i==0) return false;
return true;
}
//求解x的y次方模n的结果
int pow_mod(int x, int y, int n){
int x0=x;
for(int i=1;i<y;i++){
x*=x0;
//在每一步过程中取模,缩减数据大小,加快运算速度
//(a*b*...)%n=(a%n*b%n*...)%n
x%=n;
}
return x;
}
//求a,b最大公因数
int gcd(int a, int b){
//a<b则交换
if(a<b){
a=a+b;
b=a-b;
a=a-b;
}
//欧几里得
while(a%b){
int c=a;
a=b;
b=c%b;
}
return b;
}
int main()
{
//公钥(n,e),私钥(n,d)
int p, q, n, t, e, d;
//两位大质数p,q
cout<<"Please input two primes:";
cin>>p>>q;
while(!isprime(p)||!isprime(q)){
cout<<"Please retype!"<<endl;
cin>>p>>q;
}
n=p*q;
t=(p-1)*(q-1);
//加密密钥e
e=2;//从2开始挑选合适的密钥
while(e++)
if(gcd(e, t)==1&&e<t) break;//e,t互质,且e<t
//解密密钥d
for(int i=1;;i++){
//已知e和t,求(d*e)%t=1中的d,采用试商
//当t与商i相乘再+1,模上e之后的结果刚好为0,则说明找到了整数d
if((t*i+1)%e==0){
d=(t*i+1)/e;
break;
}
}
//至此,准备工作完成
char plain[50];//字符型明文
int plainnum[50], ciphernum[50];//利用ASCII码转换为数字型
cout<<"Please input the plaintext:";
getchar();//消除输入完p,q后的回车
gets(plain);
//RSA加密:C=(P^E)modN
for(int i=0;i<strlen(plain);i++){
plainnum[i]=(int)plain[i];//转换
ciphernum[i]=pow_mod(plainnum[i],e,n);
}
cout<<"The ciphertext is:";
for(int i=0;i<strlen(plain);i++)
//此处输出的密文为根据ASCII码转换的数字
cout<<ciphernum[i]<<" ";
cout<<endl;
//RSA解密:P=(C^D)modN
cout<<"The text afer decryption is:";
for(int i=0;i<strlen(plain);i++)
cout<<(char)pow_mod(ciphernum[i],d,n);//转换
return 0;
}
其中line61~line69,求解(d*e)%t=1中,e的模乘法逆元d时,使用了试商.
然而,数据量越大,所尝试的商越多,耗时越多.
为了时间能得到优化,此处可使用扩展欧几里得算法:
扩展欧几里得算法过程简述:
求解ax+by=gcd(a,b)中的x和y
①准备工作:
r[-1]=a, r[0]=b;
x[-1]=1, y[-1]=0;
x[0]=0, y[0]=1;
②代入公式:
r[i]=r[i-2]%r[i-1]; q[i]=r[i-2]/r[i-1];
x[i]=x[i-2]-x[i-1]*q[i]; y[i]=y[i-2]-y[i-1]*q[i];
③终止条件:
当r[i]=0时, 停止计算, 此时:
gcd(a, b)=r[i-1]; x=x[i-1]; y=y[i-1];
将扩展欧几里得算法与RSA中求解密钥d过程建立联系 :
推导:
a为被除数, b为除数.
已知扩展欧几里得算法基本式:ax+by=gcd(a,b).
若令gcd(a,b)=1, 即ax+by=1, 再利用模运算基本性质, 等式两边同时模a, 则有:
[(ax%a)+(by%a)]%a=1%a;
0+(by%a)=1;
by%a=1;
此处a和b相当于RSA中的t和e, 而我们所求的密钥d, 就是这里的y, 可以通过扩展欧几里得算法求出.
对line61~line69进行优化:
因为数组下标只能为自然数,所以代码中起始下标从0开始,而非描述中的起始下标-1,但本质上一致,仅仅是下标序号不同。
(尽管代码形式变复杂, 然而实际运算效率是提高的)
//解密密钥d
//已知e和t,求(d*e)%t=1中的d,采用扩展欧几里得算法
int r[20], x[20], y[20], Q[20], i=1;
r[0]=t;//t作为被除数
r[1]=e;//e作为除数
x[0]=1, y[0]=0;
x[1]=0, y[1]=1;
while(i++){
r[i]=r[i-2]%r[i-1];
if(!r[i]) break;//r[i]为0, 达成终止条件
Q[i]=r[i-2]/r[i-1];
x[i]=x[i-2]-x[i-1]*Q[i];
y[i]=y[i-2]-y[i-1]*Q[i];
}
//由于求出的x和y符号必定相反,结果若为负则需要转换为正
//依然利用模运算性质:
//例如:若a为负数(|a|<n), a%n=(a+n)%n, 通过加n得到正数, 不影响结果
if(y[i-1]<0) y[i-1]+=t;
d=y[i-1];
由于不确定扩展欧几里得算法需要进行多少层, 数组大小不确定, 会造成空间浪费.
可以进一步优化:
//解密密钥d
int r0, r1, r2, x0, x1, x2, y0, y1, y2, Q;
r0=t;//作为被除数
r1=e;//作为除数
x0=1, y0=0;
x1=0, y1=1;
while(1){
r2=r0%r1;//r[i]=r[i-2]%r[i-1]
if(!r2) break;
Q=r0/r1;//q[i]=r[i-2]/r[i-1]
x2=x0-x1*Q;//x[i]=x[i-2]-x[i-1]*q[i]
y2=y0-y1*Q;//y[i]=y[i-2]-y[i-1]*q[i]
//准备新一轮运算
r0=r1, r1=r2;
x0=x1, x1=x2;
y0=y1, y1=y2;
}
if(y2<0) y2+=t;
d=y2;
由于循环和递归是相通的, 因此如果再进一步转换为递归, 可简化代码形式.