2019.11.23:
这个代码一开始采用的是如《算法笔记》上等PAT中的类似题目将十进制数字转换为数组大整数(也是十进制形式)。
但是这种想法在后来实现加密的时候快速积出现了严重的弊端,和普通十进制并没有什么区别了,依旧拖慢进程大的可怕。
今天课设答辩同学们都做出了像毕设一样的作品,很完善的调用各种类实现各种混合加密,还有图形化界面,python、java、各种语言起飞。到了我这,可怜的连个运行起来都有问题。
下面的代码是256进制的刚起步,老师说不要这样转换,可以直接用long int类型读入(C++),然后直接转换为256进制。
我在下面的写法没有抛弃原来的10进制大整数。
#include
#include
#include
#include
#include
#include
using namespace std;
/*1.生成密钥对,(e,d,n),公钥是(e,n),私钥是(d,n)
步骤如下:
求n(准备两个质数p,q。这两个数不能太小,太小则会容易破解,将p乘以q就是N),
求f=(p-1)(q-1)
求e :gcd(e,f)=1
求d:e*d mod f = 1
2.加密:用(e,n):密文 c=m^e(mod n)
3.解密:用(d,n):明文 m=c^d(mod n)
*/
//定义大整数结构体,使用保存十位数字每一位的方法
struct bigint{
int d[1000];
int len;
bigint(){
memset(d,0,sizeof(d));
len=0;
}
};
bigint e,d,p,q,n;
struct bigint256{
int d[32];
int len;
bigint256(){
memset(d,0,sizeof(d));
len=0;
}
};
int compare(bigint a,bigint b){//比较a和b的大小,a大、相等、a小分别返回1,0,-1
if(a.len>b.len)return 1;//a大
else if(a.len=0;i--){
if(a.d[i]>b.d[i]) return 1;//只要有一位a大,则a大
else if(a.d[i]=0;i--){
cout<=1&&c.d[c.len-1]==0){
c.len--;//去除高位的0,同时至少保留1位最低位
}
if(sign==1)return c;
else if(sign==-1){
c.d[c.len-1]=-c.d[c.len-1];
return c;
}
}
//重载高精度加法
bigint operator + ( bigint a, bigint b)
{
if(a.d[a.len-1]<0&&b.d[b.len-1]>0){
a.d[a.len-1]=-a.d[a.len-1];
return b-a;
}
if(a.d[a.len-1]>0&&b.d[b.len-1]<0){
b.d[b.len-1]=-b.d[b.len-1];
return a-b;
}
int sign=1;
if(a.d[a.len-1]<0&&b.d[b.len-1]<0){
b.d[b.len-1]=-b.d[b.len-1];
a.d[a.len-1]=-a.d[a.len-1];
sign=-1;
}
bigint c;
int carry = 0;//carry是进位
for(int i=0;i=1&&c.d[c.len-1]==0){//清高位零
c.len--;}
if(sign==1)return c;
else if(sign==-1){
c.d[c.len-1]=-c.d[c.len-1];
return c;
}
}
//重载大整数除法
/*
思想:反复做减法,看从被除数里面最多能减去多少个除数,商就是多少。
*/
//重新写一个减法,适合于返回长度,将数组直接做减法,参数是bigint中数组
int SubStract( int *p1, int *p2, int len1, int len2 )
{
int i;
if( len1 < len2 )
return -1;
if( len1 == len2 )
{ //判断p1 > p2
for( i=len1-1; i>=0; i-- )
{
if( p1[i] > p2[i] ) //若大,则满足条件,可做减法
break;
else if( p1[i] < p2[i] ) //否则返回-1
return -1;
}
}
for( i=0; i<=len1-1; i++ ) //从低位开始做减法
{
p1[i] -= p2[i];
if( p1[i] < 0 ) //若p1<0,则需要借位
{
p1[i] += 10; //借1当10
p1[i+1]--; //高位减1
}
}
for( i=len1-1; i>=0; i-- ) //查找结果的最高位
if( p1[i] ) //最高位第一个不为0
return (i+1); //得到位数并返回
return 0; //两数相等的时候返回0
}
bigint operator / (bigint a, bigint b){//用一个例子方便写代码7546/23=328
bigint c,z;
if(a.len=0;i--){
//首先将原来位置的b的数字移到后边(高位)
if(i>=len){
b.d[i]=b.d[i-len];//与a的长度对齐,现在即是xx32 (数组是从低位到高位进行存储)
}
//如果差值到了,开始补零
else{
b.d[i]=0;
}
}
b.len=a.len;//现在b的长度和a的长度一样了,b[]="0032"即大数2300
//现在可以利用上面的不断做减法记录次数计算除法的结果了。可以知道商的位数最大是len+1,即差值+1。
//所以外层循环是0-len,内层循环对商的每一位进行计数
bigint a1=a;//为了防止改变a,新增一个变量a1,变为a的副本
for(int j=0;j<=len;j++)
{
//需要更新外层循环中b的长度,如第一次7546-2300*3=646,第二次646-230*2=186```,每次减少1
//注意b.len-1即因为运算是从低位开始“0032”即从下标0开始,
//所以在新的while循环中指针b.d要从下一位开始,即“032”,长度相应的当然要减少一个
while((c.len=SubStract(a1.d,b.d+j,a1.len,b.len-j))>=0){//对齐后如果减1次结果还是大于等于0,说明没有减净
a1.len=c.len;//更新a1的长度,即被除数的长度为结果的长度,
//如7532-2300-2300-2300=632,这个时候a1.d="236",因为SubStract函数将结果保存在了a1.d中,所以被除数自动更新,但是位数要自己手动
z.d[len-j]++;//开始保存商的结果,接着进行减法
//cout<<"运算过程:z.d[len-j]"<<"z.d["<=0;z.len--);//求出的商可能跳过高位有0,所以再重新更新商的长度,如可能求出0030,把后面多余的0去掉
//cout<<"z.len"<=1&&z.d[z.len-1]==0){
z.len--;
}
//cout<<"z.len"<15*1进位1留5,a[1]->6+25+1=32 进位3留2,a[2]->3+10+3=16 进位1 留6,a[3]->5+1=6无进位 =>6625
找到规律ans[i+j]=a[i]+a[j];
*/
bigint operator * (bigint a, bigint b){//用一个例子方便写代码7546/23=328
bigint z;
z.len=a.len+b.len;
for(int i=0;i=10) //若>=10
{
z.d[i+1]=z.d[i+1]+z.d[i]/10; //将十位上数字进位
z.d[i]=z.d[i]%10; //将个位上的数字留下
}
}
while(z.len-1>=1&&z.d[z.len-1]==0){//和上面一样清零
z.len--;
//cout<<"a1.len--;a1.len="<=0;i--)
{
cout<=0;i--)
{
//只能先正序存,后面再逆过来
z.d[z.len]+=temp.d[i]*pow(10.0,(double)d.len-i);
// cout<<" printb(z):"; printb(z);cout<>=1;//右移相当于除以2
b=b/in2;
}
while(ans.len-1>=1&&ans.d[ans.len-1]==0){//清高位零
ans.len--;}
return ans;
}
bigint Quick_Power(bigint a,bigint b,bigint c) //快速幂
{
bigint ans=in1,res=a;
//if(b==in0)return in1;
while(compare(b,in0)>0)//由于我没有重载比较运算符,但是在前面定义了compare函数,所以在这b=0则停止,应该是一样的。
{
//if(b&1)//b&1即b%2==1
if(b%in2==in1){
ans=ans*a%c;
}
a=a*a%c;
// b>>=1;//右移相当于除以2
b=b/in2;
}
while(ans.len-1>=1&&ans.d[ans.len-1]==0){//清高位零
ans.len--;}
return ans;
}
bool Miller_Rabin(bigint x) //判断素数
{
//我们可以多选择几个 a,如果全部通过,那么 x 大概率是质数。
char s5[3]="5";
char s7[3]="7";
char s19[3]="19";
char s23[3]="23";
bigint in5=change(s5);
bigint in7=change(s7);
bigint in19=change(s19);
bigint in23=change(s23);
char s11[3]="11";
char s13[3]="13";
char s17[3]="17";
char s29[3]="29";
bigint in11=change(s11);
bigint in13=change(s13);
bigint in17=change(s17);
bigint in29=change(s29);
bigint prime[10]={in2,in3,in5,in7,in11,in13,in17,in19,in23,in29};
bigint k;
bigint s=in0,t=x-in1,j;
if(x==in2) return true; //2是素数
// if(x<2||!(x&1)) return false; //如果x是偶数或者是0,1,那它不是素数
if(compare(x,in2)<0||(x%in2==in1)) return false;
// while(!(t&1)) //将x分解成(2^s)*t的样子
while(t%in2==in1)
{
s=s+in1;
// t>>=1;
t=t/in2;
}
// for(i=0;i<10&&prime[i]=1&&res.d[res.len-1]==0){//清高位零
res.len--;}
return res;
}
/*
RSA中的e不能太小同样是密码学家D. Coppersmith,其于1990年指出[3]:
用于RSA的指数e不能太小,否则存在快速算法计算得到私钥d。这个定理要用到格密码学的著名算法LLL。
再次就不多做展开了… 不过需要注意的是,由于加密算法要计算m^e,如果e太大的话加密过程可能会比较慢。
现在一般认为令e=65537是比较合适的。
https://www.zhihu.com/question/54779059?sort=created
*/
bigint ex_gcd(bigint a,bigint b,bigint &x,bigint &y) //扩展欧几里得
{
//cout<<"compare(b,in0):"< 0)
if( (compare(gcd,in1)==0)&& compare(x,in0)>0)
{
e = j;
break;
}
}
return e;//d为x.直接使用扩展欧几里得求出x后就可以用(x%m+m)%m得到(0,m)范围内的解。
}
char tmp[10000];
int readMyf(){
FILE *fp;
fp = fopen("D://日常//大学文件//大三上学期//学科//安全协议//实验//MyCry02//mingwen.txt" , "r");
fseek( fp , 0 , SEEK_END );
int file_size;
file_size = ftell( fp );
cout<<"读取到文件大小为: "<< file_size<配置属性->c/c++->代码生成->基本运行时检查 设置为默认值
char stre65537[6]="65537";//备用
bigint in65537=change(stre65537);
cout<<"\n随机生成私钥e"<>str1>>str2;
bigint num1=change(str1);
bigint num2=change(str2);
//测试加法
bigint sum=num1+num2;
cout<<"\n****测试大整数加法结果********"<
2020.1.9:后记:这个后来用了1024bit的大整数,二进制什么的,现在我已经忘光了,记得当时网上有人家写的很好的,然后我从头到尾开始分析他代码的思想,做了个分析,然后加工加工就写报告了。
小结:关于大学里面课设的话,可能外观更重要,算法做的再好,以我现在菜鸡的水平也做不了什么,所以搞个壳子是最重要的,有了壳子之后里面的算法网上都有。
希望以后做毕设可以顺利些。