2021-2022-1课程设计-第三周进展
课程设计-第三周进展
- 本周计划完成的任务
- 本周实际完成情况(代码,文档,程序运行截图...),未完成计划的原因?如何改进?
- 本周遇到的问题与解决过程(要详细)
本周计划完成的任务
- 理解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
- 添加完成后应用并退出