C/C++ 实现基于背包算法的加解密(带摘要算法)

密码学的期末大作业:基于一种数学难题,实现对信息的加密与解密,并采用一种摘要算法生成摘要,并对摘要进行加密

  • 采用的语言为 C/C++

  • 数学难题为 背包算法(详细原理可看:背包算法原理写的很好,容易理解)

  • 摘要算法采用的是MD5(copy的,因为大作业中并未对摘要算法有要求,于是就从简copy了一份)

  • 在对摘要进行加解密时,由于背包问题逆向为数学难题,无法直接反过来,所以采用了一些特殊条件

具体过程为:

基于背包问题,实现了加密解密过程,可对由字母,数字和空格组成的字符串进行加密解密。在此基础上进行了数字签名。采用了MD5 摘要算法,并对摘要进行加密传输,解密后认证相同即为认证成功。
背包利用超递增序列作私钥,加密时通过两个互素数M,N,构造不规则序列公钥,将明晚进行二进制转化后对应求和得到密文。解密时先求出N的逆元后,对密文进行取模,再对照私钥进行解密,最后得到明文。
签名时首先通过MD5算法对明文进行摘要,并用私钥对摘要进行加密。解密时先将解密到的明文进行摘要,再对被私钥加密的摘要进行解密,通过已知公钥,M,N,求出私钥,进行解密。将解密到的摘要与解密明文提取的摘要进行对比,相同即可证明该信息可靠。

代码部分

  1. 初始化部分

通过打表的方式将数字与英文字母进行对应转换,可将其转化为六位二进制数字

string mi[37] = {"000000","000001","000010","000011","000100",
			 	 "000101","000110","000111","001000","001001",
			   	 "001010","001011","001100","001101","001110",
				 "001111","010000","010001","010010","010011",
				 "010100","010101","010110","010111","011000",
				 "011001","011010","011011","011100","011101",
				 "011110","011111","100000","100001","100010",
				 "100011","100100"};

char mm[37] = {' ','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','0','1','2',
			   '3','4','5','6','7',
			   '8','9'};

变量声明:

int A[12] = {1,3,5,11,21,44,87,175,349,701,1399,2798}; //私钥 
int m; //= 5598;
int n; 
int B[12];//公钥 
int sum[3000];//加密后的信息 
int sum1[3000];//加密后的摘要 
int ans[3000];//转换后的信息 
int ans1[3000];//转换后的摘要 
int kk;//加密时分组的个数 
int kkz = 0;//摘要分组的个数 
string ming; //输入的明文 
string zhai,zhai1,zhai2;//摘要 
string zu[3000];//加密分组后的信息 
string zuz[3000];//摘要分组后的信息 
string answer,answer1;//解密后的明文 
string flag[3000];//转换后的串 
string flagz[3000];//转换后的串 

Ready():初始化函数
输出默认给定的私钥

//私钥 
	int sum = 0;
	cout << "私钥为:[ ";
	for(int i=0; i<12; i++){
		sum += A[i];
		cout << A[i] << " ";
		if(i!=11) cout << ",";
	} 
	cout <<" ]"<< endl;
	cout << endl;

生成M
M的要求为:比私钥A中数字的和大

//随机选取m
	srand((int)time(0));
	while(1){
		int h = random(10);
		if(h) {
			m = sum + h;
			break;
		} 	
	}

生成N
N的要求为:与M互为素数

srand((int)time(0));
	while(1){
		int k = random(100);
		if(gcd(m,k)==1 && k != 1){
			n = k;
			break;
		}
	} 
//最大公约数函数 
int gcd(int a ,int b){
    return b==0 ? a : gcd(b, a%b);
}

生成公钥B并输出:
公钥B是由私钥A生成,b=a*n%m

	//生成公钥 
	for(int k=0; k<12; k++){
		B[k] = (A[k]*n)%m;
	}
	cout << "生成的公钥为:[ ";
	for(int i=0; i<12; i++){
		cout << B[i] << " ";
		if(i!=11) cout << ",";
	} 
	cout <<" ]"<< endl;
	cout << endl;
	cout << "随机选取的两个互素数为:" << m << " " << n << endl; 
	cout << endl;

明文的读入:
明文存储在文件1.txt中,通过freopen()读入

	ready();
	
	cout << "请输入要加密的明文:" <<endl;
	cout << endl;
	freopen("1.txt","r",stdin);
	getline(cin,ming);
  1. 明文加密部分

函数名称:jiami()

第一部分:将明文转化成为对应六位二进制数
当输入的明文数量为单数时,会自动补零

	int l = ming.length();
	string zu1;
	//int kk = 0;
	for(int k=0; k<l; k++){		
		for(int j=0; j<37; j++){
			if(mm[j] == ming[k]){
				zu1 += mi[j];
				if(k%2!=0){
					zu[kk] = zu1;
					zu1 = "";
					kk++;
				}
				if(l%2!=0 && k==l-1){
					zu1 += "000000";
					zu[kk] = zu1;
					kk++;
				}
				break;				
			}
		}
	}

第二部分:将对应的二进制数字与公钥相对应
‘1’位的数字相加得到密文,每两个字符为一组,最后生成传输的密文,打印到屏幕上

	for(int i=0; i<kk;i++){
		int l1 = zu[i].length();
		for(int j=0; j<l1; j++){
			if(zu[i][j] == '1'){
				sum[i] += B[j];
			}
		}
		
	}
	cout << "加密后进行传输的密文为:[ " ;
	for(int i=0;i<kk;i++){
		cout << sum[i] ;
		if(i!=kk-1) cout << ",";
	}
	cout <<" ]"<< endl;
	cout << endl;
  1. 明文解密部分

解密部分默认已知私钥A,M,N

第一部分:求出N的逆元
利用辗转相除法求出N的逆元

int ni(int m,int n){
	int a, q, b[100], my;
	q = 0;
	a = m%n;
	my = m;
	while(a!=0){
		a = m%n;
		b[q] = m/n;
		q++;
		m = n;
		n = a;
	}
	int bb[100], j=2, bbb;
	bb[0]=1;
	bb[1] = b[q-2];
	int nn;
	nn=q-2;
	for(int i=nn; i>0; i--){	
		bb[j] = b[i-1]*bb[j-1] + bb[j-2];
		bbb = bb[j];
		j++;
	}
 	if((q-1)%2==0) return(bbb);
 	else return (my-bbb);
}

第二部分:通过逆元还原密文
将密文的每个值与逆元相乘再模M,再将得到的数字与私钥A相比,还原出六位二进制字符串

//已知私钥A,m,n
	if(sum[0]==0){
		cout << "请先进行加密!" << endl;
	}
	int niyuan = ni(m,n);
	for(int i=0; i<kk; i++){
		ans[i] = sum[i]*niyuan%m;
		string flag1;
		for(int j=11; j>=0; j--){
			if(ans[i]>=A[j]){
				flag1 += "1";
				ans[i] = ans[i] - A[j];
			}
			else flag1 += "0";
		}
		reverse(flag1.begin(),flag1.end());
		flag[i] = flag1;
	}

第三部分:将得到的二进制与转换表对应进行转换,得到明文
利用string的翻转函数,截取函数等,注意解密后去除末尾的空格
(由加密时补位的0造成),否则会导致后面的签名,摘要部分出现误差。

	for(int i=0; i<kk; i++){
		string flag11,flag12;
		flag11 = flag[i].substr(0,6);
		flag12 = flag[i].substr(6,11);	
		for(int i=0; i<37; i++){
			if(mi[i] == flag11){
				answer += mm[i];
			}
		}
		for(int i=0; i<37; i++){
			if(mi[i] == flag12){
				answer += mm[i];
			}
		}
	}
	if(ming.length()%2 != 0){
		answer.erase(answer.find_last_not_of(" ") + 1);
	}
	cout << "解密后的明文为:" << answer << endl;
	cout << endl;
	return answer;
  1. 摘要算法部分

摘要部分采用的算法为MD5算法,详细代码见下面代码部分。

  1. 摘要加密部分

第一部分:与明文加密部分向同,将摘要进行二进制数字转换

	int l = ming.length();
	string zu1;
	
	for(int k=0; k<l; k++){		
		for(int j=0; j<37; j++){
			if(mm[j] == ming[k]){
				zu1 += mi[j];
				if(k%2!=0){
					zuz[kkz] = zu1;
					zu1 = "";
					kkz++;
				}
				if(l%2!=0 && k==l-1){
					zu1 += "000000";
					zuz[kkz] = zu1;
					kkz++;
				}
				break;				
			}
		}
	}

第二部分:与明文加密类似,但是用的是私钥

	for(int i=0; i<kkz;i++){
		int l1 = zuz[i].length();
		for(int j=0; j<l1; j++){
			if(zuz[i][j] == '1'){
				sum1[i] += A[j];
			}
		}
		
	}
	cout << "加密后进行传输的摘要为:[ " ;
	for(int i=0;i<kkz;i++){
		cout << sum1[i] ;
		if(i!=kkz-1) cout << ",";
	}
	cout <<" ]"<< endl;
	cout << endl;
  1. 摘要解密部分

由于背包算法逆过程为数学难题,无法直接反推,所以采取用私钥加密,解密部分默认已知公钥B,M,N,反推回私钥A并进行解密的方法。

第一部分:私钥A进行还原

//已知公钥B,m,n
	answer1 = "";
	int BB[12];
	int niyuan = ni(m,n);
	for(int i=0; i<12; i++){
		BB[i] = B[i]*niyuan % m;
	}

第二部分:将密文与私钥A进行对比还原二进制数字串

for(int i=0; i<kkz; i++){
		string flag1;
		for(int j=11; j>=0; j--){
			if(sum1[i]>=BB[j]){
				flag1 += "1";
				sum1[i] = sum1[i] - BB[j];
			}
			else flag1 += "0";
		}
		reverse(flag1.begin(),flag1.end());
		flagz[i] = flag1;
	}

第三部分:将二进制数字串与转换表对应,还原出摘要

for(int i=0; i<kkz; i++){
		string flag11,flag12;
		flag11 = flagz[i].substr(0,6);
		flag12 = flagz[i].substr(6,11);	
		for(int i=0; i<37; i++){
			if(mi[i] == flag11){
				answer1 += mm[i];
			}
		}
		for(int i=0; i<37; i++){
			if(mi[i] == flag12){
				answer1 += mm[i];
			}
		}
	}

	return answer1;
  1. 主函数

通过调用上面的加解密,签名函数等实现整个项目,并对两个摘要进行对比验证,给出验证结果:

int main(){
	
	ready();
	
	cout << "请输入要加密的明文:" <<endl;
	cout << endl;
	freopen("1.txt","r",stdin);
	getline(cin,ming);
	
	jiami(ming);//加密 
	zhai = getMD5(ming);//对发送方明文进行摘要
	cout << "对发送方明文生成的摘要为:" << zhai << endl; 
	cout << endl;
	jiamizhai(zhai);//对摘要进行加密 
	
	answer = jiemi();//解密 
	zhai1 = jiemizhai();//对摘要进行解密 
	zhai2 = getMD5(answer);//对接收方解密后的明文进行摘要
	cout << "对接收方解密后的还原的摘要为:" << zhai1 << endl; 
	cout << endl;
	cout << "对接收方解密后的明文生成的摘要为:" << zhai2 << endl; 
	cout << endl;
	if(zhai1 == zhai2) cout << "认证成功!" << endl;
	else cout << "认证失败! " << endl;
	cout << endl;
} 

运行结果

完整代码

#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <string>
#include<time.h>
#define random(x) (rand()%x)

using namespace std;

int A[12] = {1,3,5,11,21,44,87,175,349,701,1399,2798}; //私钥 
int m; //= 5598;
int n; 
int B[12];//公钥 
int sum[3000];//加密后的信息 
int sum1[3000];//加密后的摘要 
int ans[3000];//转换后的信息 
int ans1[3000];//转换后的摘要 
int kk;//加密时分组的个数 
int kkz = 0;//摘要分组的个数 
string ming; //输入的明文 
string zhai,zhai1,zhai2;//摘要 
string zu[3000];//加密分组后的信息 
string zuz[3000];//摘要分组后的信息 
string answer,answer1;//解密后的明文 
string flag[3000];//转换后的串 
string flagz[3000];//转换后的串 

string mi[37] = {"000000","000001","000010","000011","000100",
			 	 "000101","000110","000111","001000","001001",
			   	 "001010","001011","001100","001101","001110",
				 "001111","010000","010001","010010","010011",
				 "010100","010101","010110","010111","011000",
				 "011001","011010","011011","011100","011101",
				 "011110","011111","100000","100001","100010",
				 "100011","100100"};

char mm[37] = {' ','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','0','1','2',
			   '3','4','5','6','7',
			   '8','9'};
			  		   

//最大公约数函数 
int gcd(int a ,int b){
    return b==0 ? a : gcd(b, a%b);
}

//辗转相除法求逆元 
int ni(int m,int n){
	int a, q, b[100], my;
	q = 0;
	a = m%n;
	my = m;
	while(a!=0){
		a = m%n;
		b[q] = m/n;
		q++;
		m = n;
		n = a;
	}
	int bb[100], j=2, bbb;
	bb[0]=1;
	bb[1] = b[q-2];
	int nn;
	nn=q-2;
	for(int i=nn; i>0; i--){	
		bb[j] = b[i-1]*bb[j-1] + bb[j-2];
		bbb = bb[j];
		j++;
	}
 	if((q-1)%2==0) return(bbb);
 	else return (my-bbb);
}

void ready(){
	
	//私钥 
	int sum = 0;
	cout << "私钥为:[ ";
	for(int i=0; i<12; i++){
		sum += A[i];
		cout << A[i] << " ";
		if(i!=11) cout << ",";
	} 
	cout <<" ]"<< endl;
	cout << endl;
	
	//随机选取m
	srand((int)time(0));
	while(1){
		int h = random(10);
		if(h) {
			m = sum + h;
			break;
		} 	
	}
	srand((int)time(0));
	while(1){
		int k = random(100);
		if(gcd(m,k)==1 && k != 1){
			n = k;
			break;
		}
	} 
	
	
	//生成公钥 
	for(int k=0; k<12; k++){
		B[k] = (A[k]*n)%m;
	}
	cout << "生成的公钥为:[ ";
	for(int i=0; i<12; i++){
		cout << B[i] << " ";
		if(i!=11) cout << ",";
	} 
	cout <<" ]"<< endl;
	cout << endl;
	cout << "随机选取的两个互素数为:" << m << " " << n << endl; 
	cout << endl;
}

//加密部分 
void jiami(string ming){
	
	int l = ming.length();
	string zu1;
	//int kk = 0;
	for(int k=0; k<l; k++){		
		for(int j=0; j<37; j++){
			if(mm[j] == ming[k]){
				zu1 += mi[j];
				if(k%2!=0){
					zu[kk] = zu1;
					zu1 = "";
					kk++;
				}
				if(l%2!=0 && k==l-1){
					zu1 += "000000";
					zu[kk] = zu1;
					kk++;
				}
				break;				
			}
		}
	}
	for(int i=0; i<kk;i++){
		int l1 = zu[i].length();
		for(int j=0; j<l1; j++){
			if(zu[i][j] == '1'){
				sum[i] += B[j];
			}
		}
		
	}
	cout << "加密后进行传输的密文为:[ " ;
	for(int i=0;i<kk;i++){
		cout << sum[i] ;
		if(i!=kk-1) cout << ",";
	}
	cout <<" ]"<< endl;
	cout << endl;
}

void jiamizhai(string ming){
	
	int l = ming.length();
	string zu1;
	
	for(int k=0; k<l; k++){		
		for(int j=0; j<37; j++){
			if(mm[j] == ming[k]){
				zu1 += mi[j];
				if(k%2!=0){
					zuz[kkz] = zu1;
					zu1 = "";
					kkz++;
				}
				if(l%2!=0 && k==l-1){
					zu1 += "000000";
					zuz[kkz] = zu1;
					kkz++;
				}
				break;				
			}
		}
	}

	for(int i=0; i<kkz;i++){
		int l1 = zuz[i].length();
		for(int j=0; j<l1; j++){
			if(zuz[i][j] == '1'){
				sum1[i] += A[j];
			}
		}
		
	}
	cout << "加密后进行传输的摘要为:[ " ;
	for(int i=0;i<kkz;i++){
		cout << sum1[i] ;
		if(i!=kkz-1) cout << ",";
	}
	cout <<" ]"<< endl;
	cout << endl;
}

//解密部分
string jiemi(){
	//已知私钥A,m,n
	if(sum[0]==0){
		cout << "请先进行加密!" << endl;
	}
	int niyuan = ni(m,n);
	for(int i=0; i<kk; i++){
		ans[i] = sum[i]*niyuan%m;
		string flag1;
		for(int j=11; j>=0; j--){
			if(ans[i]>=A[j]){
				flag1 += "1";
				ans[i] = ans[i] - A[j];
			}
			else flag1 += "0";
		}
		reverse(flag1.begin(),flag1.end());
		flag[i] = flag1;
	}
	for(int i=0; i<kk; i++){
		string flag11,flag12;
		flag11 = flag[i].substr(0,6);
		flag12 = flag[i].substr(6,11);	
		for(int i=0; i<37; i++){
			if(mi[i] == flag11){
				answer += mm[i];
			}
		}
		for(int i=0; i<37; i++){
			if(mi[i] == flag12){
				answer += mm[i];
			}
		}
	}
	if(ming.length()%2 != 0){
		answer.erase(answer.find_last_not_of(" ") + 1);
	}
	cout << "解密后的明文为:" << answer << endl;
	cout << endl;
	return answer;
}

string jiemizhai(){
	//已知公钥B,m,n
	answer1 = "";
	int BB[12];
	int niyuan = ni(m,n);
	for(int i=0; i<12; i++){
		BB[i] = B[i]*niyuan % m;
	}
	for(int i=0; i<kkz; i++){
		string flag1;
		for(int j=11; j>=0; j--){
			if(sum1[i]>=BB[j]){
				flag1 += "1";
				sum1[i] = sum1[i] - BB[j];
			}
			else flag1 += "0";
		}
		reverse(flag1.begin(),flag1.end());
		flagz[i] = flag1;
	}
	for(int i=0; i<kkz; i++){
		string flag11,flag12;
		flag11 = flagz[i].substr(0,6);
		flag12 = flagz[i].substr(6,11);	
		for(int i=0; i<37; i++){
			if(mi[i] == flag11){
				answer1 += mm[i];
			}
		}
		for(int i=0; i<37; i++){
			if(mi[i] == flag12){
				answer1 += mm[i];
			}
		}
	}

	return answer1;
}

//MD5
#define shift(x, n) (((x) << (n)) | ((x) >> (32-(n))))//右移时高位补零
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476
//strBaye的长度
unsigned int strlength;
//A,B,C,D的临时变量
unsigned int atemp;
unsigned int btemp;
unsigned int ctemp;
unsigned int dtemp;
//常量ti 
const unsigned int k[]={
        0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
        0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
        0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
        0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
        0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
        0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
        0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
        0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
        0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
        0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
        0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
        0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
        0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
//向左位移数
const unsigned int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
        12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
        4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
        15,21,6,10,15,21,6,10,15,21,6,10,15,21};
const char str16[]="0123456789abcdef";
void mainLoop(unsigned int M[])
{
    unsigned int f,g;
    unsigned int a=atemp;
    unsigned int b=btemp;
    unsigned int c=ctemp;
    unsigned int d=dtemp;
    for (unsigned int i = 0; i < 64; i++)
    {
        if(i<16){
            f=F(b,c,d);
            g=i;
        }else if (i<32)
        {
            f=G(b,c,d);
            g=(5*i+1)%16;
        }else if(i<48){
            f=H(b,c,d);
            g=(3*i+5)%16;
        }else{
            f=I(b,c,d);
            g=(7*i)%16;
        }
        unsigned int tmp=d;
        d=c;
        c=b;
        b=b+shift((a+f+k[i]+M[g]),s[i]);
        a=tmp;
    }
    atemp=a+atemp;
    btemp=b+btemp;
    ctemp=c+ctemp;
    dtemp=d+dtemp;
}
//填充函数

unsigned int* add(string str)
{
    unsigned int num=((str.length()+8)/64)+1;//以512位,64个字节为一组
    unsigned int *strByte=new unsigned int[num*16];    
    strlength=num*16;
    for (unsigned int i = 0; i < num*16; i++)
        strByte[i]=0;
    for (unsigned int i=0; i <str.length(); i++)
    {
        strByte[i>>2]|=(str[i])<<((i%4)*8);
    }
    strByte[str.length()>>2]|=0x80<<(((str.length()%4))*8);
    strByte[num*16-2]=str.length()*8;
    return strByte;
}
string changeHex(int a)
{
    int b;
    string str1;
    string str="";
    for(int i=0;i<4;i++)
    {
        str1="";
        b=((a>>i*8)%(1<<8))&0xff;   //逆序处理每个字节
        for (int j = 0; j < 2; j++)
        {
            str1.insert(0,1,str16[b%16]);
            b=b/16;
        }
        str+=str1;
    }
    return str;
}
string getMD5(string source)
{
    atemp=A;    //初始化
    btemp=B;
    ctemp=C;
    dtemp=D;
    unsigned int *strByte=add(source);
    for(unsigned int i=0;i<strlength/16;i++)
    {
        unsigned int num[16];
        for(unsigned int j=0;j<16;j++)
            num[j]=strByte[i*16+j];
        mainLoop(num);
    }
    return changeHex(atemp).append(changeHex(btemp)).append(changeHex(ctemp)).append(changeHex(dtemp));
}

int main(){
	
	ready();
	
	cout << "请输入要加密的明文:" <<endl;
	cout << endl;
	freopen("1.txt","r",stdin);
	getline(cin,ming);
	
	jiami(ming);//加密 
	zhai = getMD5(ming);//对发送方明文进行摘要
	cout << "对发送方明文生成的摘要为:" << zhai << endl; 
	cout << endl;
	jiamizhai(zhai);//对摘要进行加密 
	
	answer = jiemi();//解密 
	zhai1 = jiemizhai();//对摘要进行解密 
	zhai2 = getMD5(answer);//对接收方解密后的明文进行摘要
	cout << "对接收方解密后的还原的摘要为:" << zhai1 << endl; 
	cout << endl;
	cout << "对接收方解密后的明文生成的摘要为:" << zhai2 << endl; 
	cout << endl;
	if(zhai1 == zhai2) cout << "认证成功!" << endl;
	else cout << "认证失败! " << endl;
	cout << endl;
} 



  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值