2021-2022-1课程设计-第三周进展

课程设计-第三周进展

  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
  • 添加完成后应用并退出

posted @ 2022-01-07 21:35  绒绒的欧嘛harper  Views(79)  Comments(0Edit  收藏  举报
*/