zoukankan      html  css  js  c++  java
  • 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
    • 添加完成后应用并退出

  • 相关阅读:
    LINQ标准查询操作符(二)——Join、GroupJoin、GroupBy、Concat、
    LINQ标准查询操作符(一)——select、SelectMany、Where、OrderBy、OrderByDescending、ThenBy、ThenByDescending和Reverse
    LINQ GroupBy
    C# EF使用SqlQuery直接操作SQL查询语句或者存储过程
    mycat工作原理
    Linux系统启动过程详解
    jump堡垒机配置使用
    jumpserver 堡垒机环境搭建(图文详解)
    pip安装
    判断网站响应时间 脚本
  • 原文地址:https://www.cnblogs.com/harperhjl/p/15772151.html
Copyright © 2011-2022 走看看