?? evp.cpp
字號:
// 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 + -