• 202120221课程设计第三周进展


    课程设计-第三周进展

    1. 本周计划完成的任务
    2. 本周实际完成情况(代码,文档,程序运行截图...),未完成计划的原因?如何改进?
    3. 本周遇到的问题与解决过程(要详细)

    本周计划完成的任务

    • 理解java代码,改写c程序
    • 写关于安全报文系统发送方和接收方的加解密的程序,撰写并调试

    本周实际完成情况

    初步完成加解密,签名验签未成功需要进一步修改

    • 接收方
    点击查看代码receiver.c
    /**************************************************
    函数名:RecvSecMsg
    函数功能:接收安全报文。
    参数说明:
    sSendCert:[IN] ,发送方的数字证书
    sRecvPfx:[IN] ,接收方的私钥文件
    sPass:	[IN] ,接收方的私钥保护口令
    sInFile:[IN] ,安全报文文件路径
    sOutFile:[IN] ,输出的原文文件路径
    处理过程:假设B接收A发过来的安全报文
    1)B使用自己的私钥解密会话密钥。
    2)B使用会话密钥解密密文,得到明文
    3)B用A的证书验证A的签名,确认是A发送的数据。
    安全报文的格式:
    |------------------|--------|------------------------|------------|--------------
    |签名信息长度4Bytes|签名信息|会话密钥的密文长度4Bytes|会话密钥密文|原文数据的密文
    ***************************************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <openssl/err.h>
    #include <openssl/objects.h>
    #include <openssl/evp.h>
    #include <openssl/x509.h>
    #include <openssl/pem.h>
    #include <openssl/err.h>
    #include <openssl/pkcs12.h>
    #include <openssl/rand.h>
    
    #define FALSE 0
    #define TRUE 1
    int main(int argc, char *argv[])
    {
    	BIO *bio;
    	int rv;
    	PKCS12 *p12 = NULL;					//接收者p12结构体指针
    	X509 *RecvCert = NULL;				//接收者X509结构体指针
    	EVP_PKEY *pkey = NULL;				//接收者私钥结构体指针
    	X509 *SignCert = NULL;				//发送者(签名者)X509结构体指针
    	unsigned char *tmp;
    	unsigned char SignCertBuf[4096];	//发送者证书数组
    	unsigned int SignCertBufLen;		//发送者证书长度
    	unsigned char PfxBuf[4096];			//接收者p12数据数组
    	unsigned int PfxBufLen;				//p12数据长度
    	unsigned char Buffer[4096];			//数据缓冲区数组
    	unsigned int BufferLen;				//数据长度
    	unsigned char SessionKey[128];		//会话密钥
    	unsigned int SessionKeyLen;			//会话密钥长度
    	unsigned char CipherSessionKey[256];//密文的会话密钥
    	unsigned int CipherSessionKeyLen=0;	//密文的会话密钥的长度
    	unsigned char Sign[512];	//签名值
    	unsigned char Digest[512];
    	unsigned int DigestLen=0;
    	unsigned int SignLen=0;				//签名值长度
    	unsigned int DeSignLen=0;
    	unsigned char DeSign[512];
    	EVP_MD_CTX *mdctx;					//摘要上下文
    	char err[1024];
    
    	OpenSSL_add_all_algorithms();
    	//打开发送者证书
    	FILE *fp;
    	fp = fopen(argv[1],"rb");
    	if(fp==NULL)
        {
            fprintf(stderr,"打开发送者证书失败!");
            return 0;
        }
    	SignCertBufLen = fread(SignCertBuf,1,4096,fp);
    	fclose(fp);
    
    	//转化为X509结构体
    	tmp = SignCertBuf;
    	SignCert = d2i_X509(NULL,(const unsigned char **)&tmp,SignCertBufLen);
    	if(SignCert == NULL)
    	{
    	    printf("证书转换为x509结构体失败!");
    		return 0;
    	}
    
    	//打开接收者p12文件
    	fp = fopen(argv[2],"rb");
    	if(fp==NULL)
        {
            fprintf(stderr,"p12证书打开失败");
            return 0;
        }
    	PfxBufLen = fread(PfxBuf,1,4096,fp);
    	fclose(fp);
    	//转化为PKCS12结构体
    	bio = BIO_new(BIO_s_mem());
    	rv = BIO_write(bio,PfxBuf,PfxBufLen);
    	p12 = d2i_PKCS12_bio(bio, NULL);
    	if(p12 ==NULL)
    	{
    	    fprintf(stderr,"p12证书转换失败!");
    		X509_free(SignCert);
    		BIO_free_all(bio);
    		return 0;
    	}
    	BIO_free_all(bio);
    
    	//解析PKCS12,获取接收者的私钥和证书
    	char Passwd[1024];
    	scanf("%s",Passwd);
    	rv = PKCS12_parse(p12, Passwd,&pkey,&RecvCert,NULL);
    	if(rv != 1)
    	{
    	    fprintf(stderr,"密码错误");
    		X509_free(SignCert);
    		PKCS12_free(p12);
    		return 0;
    	}
    	//根据安全报文的文件格式,读取安全报文
    	//|------------------|--------|------------------------|------------|--------------
    	//|签名信息长度4Bytes|签名信息|会话密钥的密文长度4Bytes|会话密钥密文|原文数据的密文
    	//读取签名值,密文Sessionkey
    	fp = fopen(argv[3],"rb");
    	if(fp == NULL)
    	{
    	    fprintf(stderr,"打开加密文件失败");
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		return 0;
    	}
    	//读取签名值
    	fread(&SignLen,1,sizeof(SignLen),fp);
    
    	//if((SignLen<=0)||(SignLen >256))
    	if(SignLen<=0)
    	{
    	    fprintf(stderr,"签名错误!");
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		fclose(fp);
    		return 0;
    	}
    
    	fread(Sign,1,SignLen,fp);
    	//读取密文的会话密钥
    	fread(&CipherSessionKeyLen,1,sizeof(CipherSessionKeyLen),fp);
    	if((CipherSessionKeyLen<=0)||(CipherSessionKeyLen >256))
    	{
    	    fprintf(stderr,"读取会话密钥错误!");
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		fclose(fp);
    		return 0;
    	}
    
    	fread(CipherSessionKey,1,CipherSessionKeyLen,fp);
    	//私钥接收者私钥解密会话密钥
    	SessionKeyLen = EVP_PKEY_decrypt_old(SessionKey,CipherSessionKey,CipherSessionKeyLen,pkey);
    	if(SessionKeyLen < 0)
    	{
    	    fprintf(stderr,"会话密钥解密错误!");
    
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		fclose(fp);
    		return 0;
    	}
    
    	//利用明文的会话密钥解密安全报文
    	unsigned char out[1024+EVP_MAX_KEY_LENGTH];
    	int outl;
    	unsigned char in[1024];
    	int inl;
    	EVP_CIPHER_CTX *ctx;
    	ctx = EVP_CIPHER_CTX_new();
    	EVP_CIPHER_CTX_init(ctx);
    	FILE *fpOut;
    	fpOut = fopen(argv[4],"wb");
    	if(fpOut==NULL)
    	{
    	    fprintf(stderr,"打开解密文件失败!");
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		fclose(fp);
    		return 0;
    	}
    	//设置解密算法和密钥。
    	rv = EVP_DecryptInit_ex(ctx,EVP_sm4_ecb(),NULL,SessionKey,NULL);
    	if(rv != 1)
    	{
    	    fprintf(stderr,"解密算法初始化失败!");
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		fclose(fp);
    		fclose(fpOut);
    		EVP_CIPHER_CTX_cleanup(ctx);
    		return 0;
    	}
    	//以1024字节为单位,循环读取安全报文,解密并保存到文件。
    	for(;;)
    	{
    		inl = fread(in,1,1024,fp);//读取1024个字节
    		if(inl <= 0)
    			break;
    		rv = EVP_DecryptUpdate(ctx,out,&outl,in,inl);//解密
    		if(rv != 1)
    		{
    			EVP_PKEY_free(pkey);
    			X509_free(SignCert);
    			X509_free(RecvCert);
    			PKCS12_free(p12);
    			fclose(fp);
    			fclose(fpOut);
    			EVP_CIPHER_CTX_cleanup(ctx);
    			fprintf(stderr,"解密失败");
    			return 0;
    		}
    		fwrite(out,1,outl,fpOut);//保存到文件
    	}
    	rv = EVP_DecryptFinal_ex(ctx,out,&outl);//完成解密,输出剩余的明文。
    	if(rv != 1)
    	{
    	    fprintf(stderr,"剩余明文输出失败!");
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		fclose(fp);
    		fclose(fpOut);
    		EVP_CIPHER_CTX_cleanup(ctx);
    		return 0;
    	}
    	fwrite(out,1,outl,fpOut);
    	EVP_PKEY_free(pkey);
    	X509_free(RecvCert);
    	PKCS12_free(p12);
    	fclose(fp);
    	fclose(fpOut);
    	EVP_CIPHER_CTX_cleanup(ctx);
    
    
    	//解密完成,对原文验证签名
     	fp = fopen(argv[4],"rb");
    	if(fp==NULL)
    	{
    	    fprintf(stderr,"打开验签文件失败!");
    		X509_free(SignCert);
    		return 0;
    	}
    
    	mdctx = EVP_MD_CTX_new();
    	EVP_MD_CTX_init(mdctx);	//初始化摘要上下文
    	if(!EVP_SignInit_ex(mdctx, EVP_sm3(), NULL))	//设置摘要算法,这里选择SM3
    	{
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		fprintf(stderr,"初始化签名失败!\n");
    		fclose(fp);
    		return 0;
    	}
    	//不断循环,以4096字节为单位读取文件,并摘要
    	for(;;)
    	{
    		BufferLen=fread(Buffer,1,4096,fp);//每次读取4096个字节
    		if(BufferLen <=0)
    			break;
    		if(!EVP_SignUpdate(mdctx, Buffer, BufferLen))//摘要
    		{
    
    			EVP_PKEY_free(pkey);
    			X509_free(SignCert);
    			X509_free(RecvCert);
    			PKCS12_free(p12);
    			fclose(fp);
    			fprintf(stderr,"签名摘要生成失败EVP_SignUpdate\n");
    			return 0;
    		}
    	}
    	fclose(fp);
    	if(!EVP_DigestFinal(mdctx,Digest,&DigestLen))//完成签名,获得摘要
    	{
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		ERR_error_string(ERR_get_error(),err);
    		printf("签名摘要生成摘要失败EVP_SignFinal %s\n",err);
    		return 0;
    	}
    
    	DeSignLen = EVP_PKEY_decrypt_old(DeSign,Sign,SignLen,X509_get_pubkey(SignCert));
    	if(DeSignLen < 0)
    	{
    	    fprintf(stderr,"解签错误!");
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		fclose(fp);
    		return 0;
    	}
    
    	if(strcmp(DeSign,Digest) == 0)
        {
            fprintf(stdin,"签名验证成功!");
            X509_free(SignCert);
            EVP_MD_CTX_free(mdctx);
            return TRUE;
        }
        else
        {
            fprintf(stdin,"签名验证失败!");
            X509_free(SignCert);
            EVP_MD_CTX_free(mdctx);
            return FALSE;
        }
    
    
    
    	X509_free(SignCert);
    	EVP_MD_CTX_free(mdctx);
     	return FALSE;
    }
    
    • 发送方
    点击查看代码send.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <openssl/err.h>
    #include <openssl/objects.h>
    #include <openssl/evp.h>
    #include <openssl/x509.h>
    #include <openssl/pem.h>
    #include <openssl/err.h>
    #include <openssl/pkcs12.h>
    #include <openssl/rand.h>
    
    int main(int argc, char *argv[])
    {
        BIO *bio;
    	int rv;
    	PKCS12 *p12 = NULL;					//保存发送者私钥的PKCS12结构体指针
    	X509 *RecvCert = NULL;				//保存接收者证书的X509结构体指针
    	EVP_PKEY *pkey = NULL;				//保存发送者私钥的EVP_PKEY结构体指针
    	X509 *SignCert = NULL;				//保存发送者证书的X509结构体指针
    	unsigned char *tmp;
    	unsigned char recvCertBuf[4096];	//保存接收者证书的数组
    	unsigned int recvCertBufLen;		//接收者证书长度
    	unsigned char PfxBuf[4096];			//保存发送者P12文件的数组
    	unsigned int PfxBufLen;				//p12文件长度
    	unsigned char Buffer[4096];			//保存待处理的文件数据的数组
    	unsigned int BufferLen;				//数据长度
    	unsigned char SessionKey[128];		//加密发送文件的会话密钥
    	unsigned char CipherSessionKey[256];//会话密钥的密文
    	unsigned int CipherSessionKeyLen;	//会话密钥的密文长度
    	unsigned char Sign[512];			//待发送文件的签名值
    	unsigned int SignLen;				//签名长度
    	EVP_MD_CTX *mdctx;					//计算摘要的上下文
    	FILE *fp;							//文件句柄
    	char err[1024];
    
    
    	OpenSSL_add_all_algorithms();
    	mdctx = EVP_MD_CTX_new();
    	//打开接收者证书
    	fp = fopen(argv[1],"rb");
    	if(fp==NULL)
        {
            fprintf(stderr,"File to open %s\n", argv[1]);
            return 0;
        }
    	recvCertBufLen = fread(recvCertBuf,1,4096,fp);
    	fclose(fp);
    	//打开发送者PFX(p12)文件
    	fp = fopen(argv[2],"rb");
    	if(fp==NULL)
        {
            fprintf(stderr,"File to open %s\n", argv[2]);
            return 0;
        }
    	PfxBufLen = fread(PfxBuf,1,4096,fp);
    	fclose(fp);
    
    	//把接收者证书转化为X509结构体
    	tmp = recvCertBuf;
    	RecvCert = d2i_X509(NULL,(const unsigned char **)&tmp,recvCertBufLen);
    	if(RecvCert == NULL)
    	{
    	    fprintf(stderr,"File to transform %s\n", argv[1]);
    		return 0;
    	}
    
    	//把P12文件转化为PKCS12结构体
    	bio = BIO_new(BIO_s_mem());
    	rv = BIO_write(bio,PfxBuf,PfxBufLen);
    	p12 = d2i_PKCS12_bio(bio, NULL);
    	if(p12 ==NULL)
    	{
    		X509_free(RecvCert);
    		BIO_free_all(bio);
    		fprintf(stderr,"File to transform %s\n", argv[2]);
    		return 0;
    	}
    	BIO_free_all(bio);
    
    	char Passwd[1024];
    	printf("请输入pkcs12证书密码\n");
    	scanf("%s",&Passwd);
    	//从PKCS12结构体中解析获得私钥和证书
    	rv = PKCS12_parse(p12, Passwd,&pkey,&SignCert,NULL);
    	if(rv != 1)
    	{
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		printf("密码错误!\n");
    		return 0;
    	}
    
    	//对待发送文件签名
    	mdctx = EVP_MD_CTX_new();
    	EVP_MD_CTX_init(mdctx);	//初始化摘要上下文
    	if(!EVP_SignInit_ex(mdctx, EVP_sm3(), NULL))	//设置摘要算法,这里选择SM3
    	{
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		printf("初始化签名失败!\n");
    		return 0;
    	}
    	//打开待发送的文件
    	fp = fopen(argv[3],"rb");
    	if(fp == NULL)
    	{
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		return 0;
    	}
    	//不断循环,以4096字节为单位读取文件,并摘要
    	for(;;)
    	{
    		BufferLen=fread(Buffer,1,4096,fp);//每次读取4096个字节
    		if(BufferLen <=0)
    			break;
    		if(!EVP_SignUpdate(mdctx, Buffer, BufferLen))//签名
    		{
    
    			EVP_PKEY_free(pkey);
    			X509_free(SignCert);
    			X509_free(RecvCert);
    			PKCS12_free(p12);
    			fclose(fp);
    			printf("签名失败EVP_SignUpdate\n");
    			return 0;
    		}
    	}
    	fclose(fp);
    	if(!EVP_DigestFinal(mdctx,Sign,&SignLen))//完成签名,获得签名值
    	{
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		ERR_error_string(ERR_get_error(),err);
    		printf("摘要失败EVP_SignFinal %s\n",err);
    		return 0;
    	}
    	unsigned char real_Sign[512];
    	unsigned int real_Sign_Len;
    	real_Sign_Len = EVP_PKEY_encrypt_old(real_Sign,Sign,SignLen,pkey);
    
    /*
    	if(!EVP_SignFinal(mdctx,Sign,&SignLen,pkey))//完成签名,获得签名值
    	{
    		EVP_PKEY_free(pkey);
    		X509_free(SignCert);
    		X509_free(RecvCert);
    		PKCS12_free(p12);
    		ERR_error_string(ERR_get_error(),err);
    		printf("签名失败EVP_SignFinal %s\n",err);
    		return 0;
    	}
    */
    
    	//释放密钥和证书以及pkcs12结构体
    	EVP_PKEY_free(pkey);
    	X509_free(SignCert);
    	PKCS12_free(p12);
    
    	//产生随机数,作为会话密钥
    	RAND_bytes(SessionKey,128);
    	//使用接收者证书公钥加密会话密钥
    	CipherSessionKeyLen = EVP_PKEY_encrypt_old(CipherSessionKey,SessionKey,128,X509_get_pubkey(RecvCert));
    	if(CipherSessionKeyLen <= 0)
    	{
    		X509_free(RecvCert);
    		return 0;
    	}
    	X509_free(RecvCert);
    	//利用会话密钥加密原文,并输出到密文到文件
    	FILE *fpIn;
    	FILE *fpOut;
    	fpIn = fopen(argv[3],"rb");
    	if(fpIn == NULL)
    	{
    		return 0;
    	}
    
    	fpOut = fopen(argv[4],"wb");
    	if(fpOut == NULL)
    	{
    		return 0;
    	}
    
    	//密文文件格式:
    	// |------------------|--------|------------------------|------------|--------------
    	// |签名信息长度4Bytes|签名信息|会话密钥的密文长度4Bytes|会话密钥密文|原文数据的密文
    	fwrite(&real_Sign_Len,1,sizeof(real_Sign_Len),fpOut);//写签名长度到文件
    	fwrite(real_Sign,1,real_Sign_Len,fpOut);			//写签名值到文件
    	fwrite(&CipherSessionKeyLen,1,sizeof(CipherSessionKeyLen),fpOut);	//写密文的会话密钥的长度到文件
    	fwrite(CipherSessionKey,1,CipherSessionKeyLen,fpOut);//写密文的会话密钥到文件
    
    	EVP_CIPHER_CTX *ctx;
    	ctx = EVP_CIPHER_CTX_new();
    	unsigned char out[1024];
    	int outl;
    	unsigned char in[1024];
    	int inl;
    	EVP_CIPHER_CTX_init(ctx);//初始化密码算法上下文
    	//设置密码算法和密钥,这里采用128位的sm4算法。
    	rv = EVP_EncryptInit_ex(ctx,EVP_sm4_ecb(),NULL,SessionKey,NULL);
    	if(rv != 1)
    	{
    		EVP_CIPHER_CTX_cleanup(ctx);
    		return 0;
    	}
    	//以1024字节为单位,循环读取原文,并加密,然后输出到密文文件。
    	for(;;)
    	{
    		inl = fread(in,1,1024,fpIn);//读取1024字节
    		if(inl <= 0)
    			break;
    		rv = EVP_EncryptUpdate(ctx,out,&outl,in,inl);//加密
    		if(rv != 1)
    		{
    			fclose(fpIn);
    			fclose(fpOut);
    			EVP_CIPHER_CTX_cleanup(ctx);
    			return 0;
    		}
    		fwrite(out,1,outl,fpOut);//输出密文到文件
    	}
    	rv = EVP_EncryptFinal_ex(ctx,out,&outl);//完成加密,输出剩余的密文。
    	if(rv != 1)
    	{
    		fclose(fpIn);
    		fclose(fpOut);
    		EVP_CIPHER_CTX_cleanup(ctx);
    		return 0;
    	}
    	fwrite(out,1,outl,fpOut);//写文件
    	fclose(fpIn);
    	fclose(fpOut);
    	EVP_CIPHER_CTX_cleanup(ctx);
    	return 0;
    }
    
    
    

    本周遇到的问题与解决过程

    问题1

    vs2022运行程序会出现找不到openssl相关文件以及函数的问题。经过查阅资料,问题的原因可能是vs2022没有配置openssl环境

    解决办法:为vs2022配置openssl

    ①在网上下载windows环境下的openssl包

    ②安装openssl




    ③在vs2022中配置openssl环境
    参考链接https://blog.csdn.net/zhizhengguan/article/details/112846817

    • 右击工程,选择“属性”

    • 选中“Include Directories”,点击右边的下拉按钮,点击“Edit…”

    • 同样的方法,将安装目录下的“lib”文件夹添加到“Library Directories”中

    • 将OpenSSL安装目录下bin文件夹中的“libcrypto-1_1-x64.dll”和“libssl-1_1-x64.dll”(名字后面的版本号可能因更新而不同)复制到工程目录下

    • 将工程平台调整为自己需要的平台,这里演示x64平台

    ④在程序里加上这两行代码

    #pragma comment(lib,"libssl.lib")
    #pragma comment(lib,"libcrypto.lib")
    

    ⑤重新运行代码后openssl 相关参数没有报错

    问题2

    qt找不到openssl相关文件以及函数的问题

    解决:进行qt的openssl配置

    使用locate找到libssl.so libcrypto.so这两个文件的路径
    在.pro文件中添加以下代码(路径根据自己安装情况进行修改)

    INCLUDEPATH += /usr/local/ssl/include
    LIBS += /usr/lib/x86_64-linux-gnu/libssl.so  /usr/lib/x86_64-linux-gnu/libcrypto.so
    

    问题3

    在编译代码时出现错误信息,This function or variable may be unsafe

    解决:

    • 在属性页面中找到“C/C++"——”预处理器“
    • 在下面的编辑窗口中添加一句命令:_CRT_SECURE_NO_WARNINGS
    • 添加完成后应用并退出

  • 相关阅读:
    基于Metaweblog API 接口一键发布到国内外主流博客平台
    uva144 Student Grants
    Uva 10452
    Uva 439 Knight Moves
    Uva 352 The Seasonal War
    switch语句
    java——基础知识
    我的lua学习2
    codeforces 431 D. Random Task 组合数学
    codeforces 285 D. Permutation Sum 状压 dfs打表
  • 原文地址:https://www.cnblogs.com/harperhjl/p/15772151.html
Copyright © 2020-2023  润新知