⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 evp.cpp

📁 关于小型CA系统的若干说明和DLL源码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// Evp.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include <OPENSSL/evp.h>
#include <openssl/pem.h>
#include <string.h>
#include <io.h>
#include "evp.h"
#include <openssl/pkcs12.h>
#include <openssl/rand.h>



BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    return TRUE;
}

/*此函数可以将DER、PEM、P12文件公钥读出来*/
X509 *load_cert(BIO *cert/*输入BIO*/, int format/*格式*/,char * pwd,/*P12密码*/
				char * outMsg) //从DER、PEM、P12格式中读取公钥证书
{
	X509 * x=NULL;
	if 	(format == DER)
		x=d2i_X509_bio(cert,NULL);
	else if (format == PEM)
		x=PEM_read_bio_X509(cert,NULL,NULL,NULL);//PEM_read_bio_X509_AUX
	else if (format == P12)
	{
		PKCS12 *p12 = d2i_PKCS12_bio(cert, NULL);
		PKCS12_parse(p12, pwd, NULL, &x, NULL);
		PKCS12_free(p12);
		p12 = NULL;
	}
	else
	{
		sprintf(outMsg,"bad input format specified for input cert\n");
		goto end;
	}
end:
	if (x == NULL)
	{
		sprintf(outMsg,"unable to load certificate\n");
	}
	return(x);
}

X509 * LoadCert(char * cert,int certlen,char * outMsg)//枚举DER/PEM格式
{
	BIO * in=NULL;
	X509 * x509=NULL;

	if(certlen==0)//输入为磁盘文件
	{
		if((in=BIO_new_file(cert, "r")) == NULL)
		{
			sprintf(outMsg,"open CA certificate file error");
			return NULL;
		}
	}
	else//输入为内存中文件
	{
		if((in=BIO_new_mem_buf(cert,certlen))== NULL)//只读类型
		{
			sprintf(outMsg,"Make Mem Bio Error");
			return NULL;
		}
	}
	if((x509=load_cert(in,DER,NULL,outMsg))==NULL)//尝试DER
	{
		BIO_reset(in);//恢复bio
		x509=load_cert(in,PEM,NULL,outMsg);//尝试PEM
	}
	if (in != NULL) BIO_free(in);
	return x509;
}

EVP_PKEY *load_key(BIO *bio, int format, char *pass,char * outMsg)//枚举DER/PEM格式
{
	EVP_PKEY *pkey=NULL;

	if (format == DER)
	{
		pkey=d2i_PrivateKey_bio(bio, NULL);
	}
	else if (format == PEM)
	{
		pkey=PEM_read_bio_PrivateKey(bio,NULL,NULL,pass);
	}
	else if (format == P12)
	{
		PKCS12 *p12 = d2i_PKCS12_bio(bio, NULL);
		PKCS12_parse(p12, pass, &pkey, NULL, NULL);
		PKCS12_free(p12);
		p12 = NULL;
	}
	else
	{
		sprintf(outMsg,"bad input format specified for key\n");
		goto end;
	}
end:
	if (pkey == NULL)
		sprintf(outMsg,"unable to load Private Key\n");
	return(pkey);
}

EVP_PKEY * LoadKey(char * key,int keylen,char * pass,char * outMsg)
{
	EVP_PKEY *pkey=NULL;
	BIO * in=NULL;

	if(keylen==0)//输入为磁盘文件
	{
		if((in=BIO_new_file(key, "r")) == NULL)
		{
			sprintf(outMsg,"open CA certificate file error");
			return NULL;
		}
	}
	else//输入为内存中文件
	{
		if((in=BIO_new_mem_buf(key,keylen))== NULL)//只读类型
		{
			sprintf(outMsg,"Make Mem Bio Error");
			return NULL;
		}
	}

	if((pkey=load_key(in,DER,pass,outMsg))==NULL)//尝试DER
	{
		BIO_reset(in);//BIO是可读写的,那么该BIO所有数据都会被清空;
						//如果该BIO是只读的,那么该操作只会简单将指
						//针指向原始位置,里面的数据可以再读.
		pkey=load_key(in,PEM,pass,outMsg);//尝试PEM
	}
	if (in != NULL) BIO_free(in);
	return pkey;
}

/*对称算法*/
/*
关于加密模式
	电子密码本 Electronic Code Book(ECB) -每64位使用的密钥都是相同的,密文跟明文一一对
										 应的关系,容易破解
	加密块链   Cipher Block Chaining(CBC)-需要一个初始化向量,密文跟明文不是一一对应的
	                                     关系,破解起来更困难,缺点:不能实时解密,也就是说,
										 必须等到每8个字节都接受到之后才能开始加密
    加密反馈   Cipher Feedback Mode(CFB)-能够实现字符的实时加密和解密,1个比特发生错误了,
										会影响其本身对应的8个字节的数据,鲁棒性太差
    输出反馈   Output Feedback Mode(OFB)-没有采用密文作为加密的数据;1个比特发生错误了,
										那么只会影响其本身对应的一个比特;数据之间相关性小,
										这种加密模式是比较不安全的,一般不提倡*/

bool Crypt(char * cpname/*算法名称*/,char * filein/*输入文件*/, char * fileout/*输出文件*/,
		   char * pwd/*用于初始iv的密码*/, int type/*1-加密、0-解密*/,char * outMsg,
		   PDrawProg DrawProg/*回调函数*/)
{
	unsigned char inbuf[1024]="";
	unsigned char outbuf[1024 + EVP_MAX_BLOCK_LENGTH]="";//所有算法最长的块长度。
	unsigned char key[EVP_MAX_KEY_LENGTH]="";//算法最长的KEY长度
    unsigned char iv[EVP_MAX_IV_LENGTH]="";//算法最长的IV长度
	int inlen=0, outlen=0;
	bool ret=true;
	const EVP_CIPHER *cipher=NULL;
	EVP_CIPHER_CTX ctx;
	memset(&ctx,0,sizeof(ctx));
	double fileLen=0;//文件长度
	double finishLen=0;//完成长度
	FILE *outfd,*infd;
	
	if(strlen(filein)==0||strlen(fileout)==0)
	{
		strcpy(outMsg,"NO specify input or output file");
		return false;
	}

    if ((infd = fopen (filein, "rb")) == NULL)//原文
	{
		strcpy(outMsg,"open input file error");
		return false;
	}
	fileLen=filelength(fileno(infd));//得到文件长度
	if ((outfd = fopen (fileout, "wb")) == NULL)//密文
	{
		strcpy(outMsg,"open output file error");
		fclose(infd);
		return false;
	}

	OpenSSL_add_all_algorithms();//add digests   and ciphers
	cipher=EVP_get_cipherbyname(cpname);
	if(cipher==NULL)
	{
		sprintf(outMsg,"Unknown cipher name %s\n",cpname);
		ret=false;
		goto err;
	}
	
	//表示產生的 key 是給哪一種 cipher 用的
	//過程中所使用的 hash 演算法,
	//用來加密的 salt,至少為八個 bytes,否則必須NULL,
	//重覆幾次 hashing 的動作,愈多次愈花時間,但相對地也愈安全
	if(!EVP_BytesToKey(cipher,EVP_md5(),NULL,(unsigned char *)pwd,
		strlen(pwd),1,key,iv))
	{
		strcpy(outMsg,"初始化key or iv 失败");
		ret=false;
		goto err;
	}
	
	EVP_CIPHER_CTX_init(&ctx);//初始化一个EVP_CIPHER_CTX结构体
	
	//type为1,则加密;如果type为0,则解密;如果type是-1,则不改变数据
	if(!EVP_CipherInit_ex(&ctx, cipher, NULL/*NULL使用缺省的实现算法*/,key,iv,type))//初始化
	{
		strcpy(outMsg,"初始化加解密结构体失败");
		ret=false;
		goto err;		
	}
	
	//该函数进行加密算法结构EVP_CIPHER_CTX密钥长度的设置。
	//如果算法是一个密钥长度固定的算法,
	//那么如果设置的密钥长度跟它固定的长度不一致,就会产生错误。
//	EVP_CIPHER_CTX_set_key_length(&ctx, 10); 		
//	EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, type);
	
	for(;;)
	{                       
		inlen = fread(inbuf, sizeof(char), 1024, infd);
		if(inlen <= 0) break;
		if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))//中間過程 
		{
			/* Error */              
			strcpy(outMsg,"中间过程错误");
			ret=false;
			goto err;
		}
		fwrite(outbuf, sizeof(char), outlen, outfd);
		finishLen+=inlen;
		if(DrawProg) DrawProg(finishLen*100/fileLen);
	}               
	if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))    //最終步驟-处理最后(Final)的一段数据
	{   /* Error */                       
		strcpy(outMsg,"最终过程错误"); 
		ret=false;
		goto err;
	}               
	fwrite(outbuf, sizeof(char), outlen, outfd);
err:
	fclose(infd);
	fclose(outfd);
	if(ctx.cipher) EVP_CIPHER_CTX_cleanup(&ctx); //清除所有信息释放内存
	EVP_cleanup();//frees all three stacks and sets their pointers to NULL ---- EVP_CIPHER
	return ret;
}

/*消息摘要*/
bool Digest(char * mdname/*摘要算法*/,char * filein/*输入文件*/,unsigned char * md_value/*返回摘要*/,
			unsigned int * md_len/*摘要长度*/, char * outMsg,PDrawProg DrawProg/*回调函数*/)
{
	const EVP_MD *md=NULL;
	char inbuf[1024]="";
	int len=0;
	bool ret=true;
	EVP_MD_CTX md_ctx;
	memset(&md_ctx,0,sizeof(md_ctx));
	FILE *infd;
	double fileLen=0;//文件长度
	double finishLen=0;//完成长度

	if(strlen(filein)==0)
	{
		strcpy(outMsg,"NO specify input or output file");
		return false;
	}

    if ((infd = fopen (filein, "rb")) == NULL)//原文
	{
		strcpy(outMsg,"open input file error");
		return false;
	}
	fileLen=filelength(fileno(infd));//得到文件长度

	//使EVP_Digest系列函数支持所有有效的信息摘要算法
	OpenSSL_add_all_digests();
    
	//根据输入的信息摘要函数的名字得到相应的EVP_MD算法结构
	md = EVP_get_digestbyname(mdname);  
	if(!md) 
	{
		sprintf(outMsg,"Unknown message digest %s",mdname);
		ret=false;
		goto err;
	}
	//初始化信息摘要结构md_ctx。
	EVP_MD_CTX_init(&md_ctx);

	//使用md的算法结构设置mdctx结构,impl为NULL,即使用缺省实现的算法(openssl本身提供的信息摘要算法)
	if(!EVP_DigestInit_ex(&md_ctx, md, NULL/*impl*/))
	{
		strcpy(outMsg,"初始化算法结构错误");
		ret=false;
		goto err;
	}

	//信息摘要运算
	for(;;)
	{
		len = fread(inbuf, 1, 1024, infd);
		if(len <= 0) break;          
		if(!EVP_DigestUpdate(&md_ctx, inbuf, len))
		{
			strcpy(outMsg,"中间过程错误");
			ret=false;
			goto err;
		}
		finishLen+=len;
		if(DrawProg) DrawProg(finishLen*100/fileLen);
	}
	//完成信息摘要计算过程,将完成的摘要信息存储在md_value里面,长度信息存储在md_len里面
	if(!EVP_DigestFinal_ex(&md_ctx, md_value, md_len))
	{	
		strcpy(outMsg,"最终过程错误");
		ret=false;
	}

err:
	//使用该函数释放mdctx占用的资源,如果使用_ex系列函数,这是必须调用的。
	fclose(infd);
	if(md_ctx.digest) EVP_MD_CTX_cleanup(&md_ctx);
	EVP_cleanup();
	return ret;
}

/*数字签名*/
bool Sign(char * key/*私钥*/,int keylen/*0-内存内容*/,char * mdname/*签名算法*/,
		  char * filein/*输入文件*/,char * fileout/*输出文件*/,
		  char * outMsg,PDrawProg DrawProg/*回调函数*/)
{
	bool ret=true;
	unsigned char * sig_buf=NULL;
	unsigned int sig_len,len=0;
	unsigned char inbuf[1024]="";
	EVP_PKEY * pkey=NULL;
	const EVP_MD *md=NULL;
	EVP_MD_CTX md_ctx;
	memset(&md_ctx,0,sizeof(md_ctx));
	BIO *in=NULL;
	FILE *outfd,*infd;
	double fileLen=0;//文件长度
	double finishLen=0;//完成长度
	if(strlen(filein)==0||strlen(fileout)==0)
	{
		strcpy(outMsg,"NO specify input or output file");
		return false;
	}
	
    if ((infd = fopen (filein, "rb")) == NULL)//原文
	{
		strcpy(outMsg,"open input file error");
		return false;
	}
	if ((outfd = fopen (fileout, "wb")) == NULL)//密文
	{
		strcpy(outMsg,"open output file error");
		fclose(infd);
		return false;
	}
	fileLen=filelength(fileno(infd));//得到文件长度


	OpenSSL_add_all_digests();

	pkey=LoadKey(key,keylen,NULL,outMsg);
	if (pkey == NULL)
	{ 
		ret=false;
		goto err;
	}
	
	/* Do the signature */
	md = EVP_get_digestbyname(mdname);  
	if(!md) 
	{
		sprintf(outMsg,"Unknown message digest %s",mdname);
		ret=false;
		goto err;
	}
	EVP_MD_CTX_init(&md_ctx);
	if(!EVP_SignInit_ex(&md_ctx,md,NULL))
	{
		strcpy(outMsg,"初始化算法结构出错");
		ret=false;
		goto err;
	}

	for(;;)
	{
		len = fread(inbuf, sizeof(char), 1024, infd);
		if(len <= 0) break;
		if(!EVP_SignUpdate(&md_ctx, inbuf, len))
		{
			sprintf(outMsg,"中间过程出错");
			ret=false;
			goto err;
		}
		finishLen+=len;
		if(DrawProg) DrawProg(finishLen*100/fileLen);
	}

	sig_len =EVP_PKEY_size(pkey);
	sig_buf=new unsigned char[sig_len];

	// 簽名後的結果,必須是個夠大視 private key 長度不同而不同,可以 EVP_PKEY_size() 取得
	if(!EVP_SignFinal (&md_ctx, sig_buf, &sig_len, pkey))
	{
		sprintf(outMsg,"最终过程出错");
		ret=false;
	}
	fwrite(sig_buf,sizeof(char),sig_len,outfd);

err:
	fclose(infd);
	fclose(outfd);
	BIO_free(in);

	if(pkey) EVP_PKEY_free (pkey);
	if(md_ctx.digest) EVP_MD_CTX_cleanup(&md_ctx);
	delete [] sig_buf;
	EVP_cleanup();
	return ret;
}

/*数字签名验证*/
bool VerifySign(char * cert/*公钥*/,int certlen,char * mdname/*签名算法*/,char * filesource,/*原始文件*/ 
				char * filesign/*签名结果*/,char * outMsg,PDrawProg DrawProg/*回调函数*/)
{
	const EVP_MD *md=NULL;
	unsigned char inbuf[1024]="";
	bool ret=true;
	unsigned char * sig_buf=NULL;
	unsigned int sig_len,len=0;
	FILE *outfd,*infd;
	double fileLen=0;//文件长度
	double finishLen=0;//完成长度
	X509 *	x509=NULL;
	EVP_PKEY * pcert=NULL;
	EVP_MD_CTX md_ctx;
	memset(&md_ctx,0,sizeof(md_ctx));

	if(strlen(filesource)==0||strlen(filesign)==0)
	{
		strcpy(outMsg,"NO specify input or output file");
		return false;
	}
	
    if ((infd = fopen (filesource, "rb")) == NULL)//原文
	{
		strcpy(outMsg,"open input file error");
		return false;
	}
	if ((outfd = fopen (filesign, "rb")) == NULL)//密文
	{
		strcpy(outMsg,"open output file error");
		fclose(infd);
		return false;
	}
	fileLen=filelength(fileno(infd));//得到文件长度

	OpenSSL_add_all_digests();
	x509=LoadCert(cert,certlen,outMsg);
	if (x509 == NULL)
	{
		ret=false;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -