EVP(Enveloped Public Key)是 OpenSSL 中用于提供对称加密、非对称加密和哈希功能的高级加密接口。EVP 库提供了一个抽象的加密框架,使得可以在不同的算法实现之间进行切换,而不需要改变应用程序的代码。以下是一些 EVP 开发的主要方面:
一、EVP基本介绍
1. EVP 加密和解密
EVP 提供了通用的加密和解密函数,可以用于对称加密和非对称加密。一般的流程如下:
- 选择加密算法,创建相应的
EVP_CIPHER
结构。 - 初始化
EVP_CIPHER_CTX
上下文。 - 使用
EVP_EncryptInit_ex
或EVP_DecryptInit_ex
初始化加密或解密操作。 - 使用
EVP_EncryptUpdate
或EVP_DecryptUpdate
处理数据。 - 使用
EVP_EncryptFinal_ex
或EVP_DecryptFinal_ex
完成加密或解密操作。
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher = EVP_aes_256_cbc();ctx = EVP_CIPHER_CTX_new();EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv);
EVP_EncryptUpdate(ctx, ciphertext, &outlen, plaintext, plaintext_len);
EVP_EncryptFinal_ex(ctx, ciphertext + outlen, &tmplen);EVP_CIPHER_CTX_free(ctx);
2. EVP 签名和验证
EVP 提供了通用的签名和验证函数,可以用于不同的哈希算法和签名算法。一般的流程如下:
- 选择哈希算法和签名算法,创建相应的
EVP_MD
和EVP_PKEY
结构。 - 初始化
EVP_MD_CTX
上下文。 - 使用
EVP_DigestInit_ex
初始化哈希操作。 - 使用
EVP_DigestUpdate
处理数据。 - 使用
EVP_DigestFinal_ex
完成哈希操作。 - 使用
EVP_SignInit_ex
或EVP_VerifyInit_ex
初始化签名或验证操作。 - 使用
EVP_SignUpdate
或EVP_VerifyUpdate
处理数据。 - 使用
EVP_SignFinal
或EVP_VerifyFinal
完成签名或验证操作。
EVP_MD_CTX *mdctx;
const EVP_MD *md = EVP_sha256();
EVP_PKEY *pkey;
unsigned char signature[256];
size_t sig_len;mdctx = EVP_MD_CTX_new();
pkey = load_private_key();EVP_DigestInit_ex(mdctx, md, NULL);
EVP_DigestUpdate(mdctx, data, data_len);
EVP_DigestFinal_ex(mdctx, digest, &digest_len);EVP_SignInit_ex(mdctx, EVP_sha256(), NULL);
EVP_SignUpdate(mdctx, data, data_len);
EVP_SignFinal(mdctx, signature, &sig_len, pkey);EVP_MD_CTX_free(mdctx);
3. EVP 加解密文件
EVP 还提供了一些便捷的函数用于对文件进行加解密操作,例如 EVP_EncryptInit_ex
、EVP_DecryptUpdate
、EVP_EncryptFinal_ex
等。
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher = EVP_aes_256_cbc();ctx = EVP_CIPHER_CTX_new();EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv);
EVP_EncryptUpdate(ctx, ciphertext, &outlen, plaintext, plaintext_len);
EVP_EncryptFinal_ex(ctx, ciphertext + outlen, &tmplen);EVP_CIPHER_CTX_free(ctx);
这些是 EVP 库的一些基本用法。具体的使用取决于你的需求,可以根据 OpenSSL 的文档和示例代码进一步了解。
二、源码结构
evp源码位于crypto/evp目录,可以分为如下几类:
2.1 全局函数
主要包括c_allc.c、c_alld.c、c_all.c以及names.c。他们加载openssl支持的所有的对称算法和摘要算法,放入到哈希表中。实现了OpenSSL_add_all_digests、OpenSSL_add_all_ciphers以及OpenSSL_add_all_algorithms(调用了前两个函数)函数。在进行计算时,用户也可以单独加载摘要函数(EVP_add_digest)和对称计算函数(EVP_add_cipher)。
2.2 BIO扩充
包括bio_b64.c、bio_enc.c、bio_md.c和bio_ok.c,各自实现了BIO_METHOD方法,分别用于base64编解码、对称加解密以及摘要。
2.3 摘要算法EVP封装
由digest.c实现,实现过程中调用了对应摘要算法的回调函数。各个摘要算法提供了自己的EVP_MD静态结构,对应源码为m_xxx.c。
2.4 对称算法EVP封装
由evp_enc.c实现,实现过程调用了具体对称算法函数,实现了Update操作。各种对称算法都提供了一个EVP_CIPHER静态结构,对应源码为e_xxx.c。需要注意的是,e_xxx.c中不提供完整的加解密运算,它只提供基本的对于一个block_size数据的计算,完整的计算由evp_enc.c来实现。当用户想添加一个自己的对称算法时,可以参考e_xxx.c的实现方式。一般用户至少需要实现如下功能:
Ø 构造一个新的静态的EVP_CIPHER结构;
Ø 实现EVP_CIPHER结构中的init函数,该函数用于设置iv,设置加解密标记、以及根据外送密钥生成自己的内部密钥;
Ø 实现do_cipher函数,该函数仅对block_size字节的数据进行对称运算;
Ø 实现cleanup函数,该函数主要用于清除内存中的密钥信息。
2.5 非对称算法EVP封装
主要是以p_开头的文件。其中,p_enc.c封装了公钥加密;p_dec.c封装了私钥解密;p_lib.c实现一些辅助函数;p_sign.c封装了签名函数;p_verify.c封装了验签函数;p_seal.c封装了数字信封;p_open.c封装了解数字信封。
2.6 基于口令的加密
包括p5_crpt2.c、p5_crpt.c和evp_pbe.c。
三、开发实例
3.1 示例1
#include <string.h>#include <openssl/evp.h>int main(){int ret,which=1;EVP_CIPHER_CTX ctx;const EVP_CIPHER *cipher;unsigned char key[24],iv[8],in[100],out[108],de[100];int i,len,inl,outl,total=0;for(i=0;i<24;i++){memset(&key[i],i,1);}for(i=0;i<8;i++){memset(&iv[i],i,1);}for(i=0;i<100;i++){memset(&in[i],i,1);}EVP_CIPHER_CTX_init(&ctx);printf("please select :\n");printf("1: EVP_des_ede3_ofb\n");printf("2: EVP_des_ede3_cbc\n");scanf("%d",&which);if(which==1)cipher=EVP_des_ede3_ofb();elsecipher=EVP_des_ede3_cbc();ret=EVP_EncryptInit_ex(&ctx,cipher,NULL,key,iv);if(ret!=1){printf("EVP_EncryptInit_ex err1!\n");return -1;}inl=50;len=0;EVP_EncryptUpdate(&ctx,out+len,&outl,in,inl);len+=outl;EVP_EncryptUpdate(&ctx,out+len,&outl,in+50,inl);len+=outl;EVP_EncryptFinal_ex(&ctx,out+len,&outl);len+=outl;printf("加密结果长度:%d\n",len);/* 解密 */EVP_CIPHER_CTX_cleanup(&ctx);EVP_CIPHER_CTX_init(&ctx);ret=EVP_DecryptInit_ex(&ctx,cipher,NULL,key,iv);if(ret!=1){printf("EVP_DecryptInit_ex err1!\n");return -1;}total=0;EVP_DecryptUpdate(&ctx,de+total,&outl,out,44);total+=outl;EVP_DecryptUpdate(&ctx,de+total,&outl,out+44,len-44);total+=outl;ret=EVP_DecryptFinal_ex(&ctx,de+total,&outl);total+=outl;if(ret!=1){EVP_CIPHER_CTX_cleanup(&ctx);printf("EVP_DecryptFinal_ex err\n");return -1;}if((total!=100) || (memcmp(de,in,100))){printf("err!\n");return -1;}EVP_CIPHER_CTX_cleanup(&ctx);printf("test ok!\n");return 0;}
输出结果如下:
please select :1: EVP_des_ede3_ofb2: EVP_des_ede3_cbc1加密结果长度:100test ok!please select :1: EVP_des_ede3_ofb2: EVP_des_ede3_cbc2加密结果长度:104test ok!
3.2 示例2
#include <string.h>#include <openssl/evp.h>int main(){int cnid,ret,i,msize,mtype;int mpktype,cbsize,mnid,mbsize;const EVP_CIPHER *type;const EVP_MD *md;int datal,count,keyl,ivl;unsigned char salt[20],data[100],*key,*iv;const char *cname,*mname;type=EVP_des_ecb();cnid=EVP_CIPHER_nid(type);cname=EVP_CIPHER_name(type);cbsize=EVP_CIPHER_block_size(type);printf("encrypto nid : %d\n",cnid);printf("encrypto name: %s\n",cname);printf("encrypto bock size : %d\n",cbsize);md=EVP_md5();mtype=EVP_MD_type(md);mnid=EVP_MD_nid(md);mname=EVP_MD_name(md);mpktype=EVP_MD_pkey_type(md);msize=EVP_MD_size(md);mbsize=EVP_MD_block_size(md);printf("md info : \n");printf("md type : %d\n",mtype);printf("md nid : %d\n",mnid);printf("md name : %s\n",mname);printf("md pkey type : %d\n",mpktype);printf("md size : %d\n",msize);printf("md block size : %d\n",mbsize);keyl=EVP_CIPHER_key_length(type);key=(unsigned char *)malloc(keyl);ivl=EVP_CIPHER_iv_length(type);iv=(unsigned char *)malloc(ivl);for(i=0;i<100;i++)memset(&data[i],i,1);for(i=0;i<20;i++)memset(&salt[i],i,1);datal=100;count=2;ret=EVP_BytesToKey(type,md,salt,data,datal,count,key,iv);printf("generate key value: \n");for(i=0;i<keyl;i++)printf("%x ",*(key+i));printf("\n");printf("generate iv value: \n");for(i=0;i<ivl;i++)printf("%x ",*(iv+i));printf("\n");return 0;}
EVP_BytesToKey函数通过salt以及data数据来生成所需要的key和iv。
输出:
encrypto nid : 29encrypto name: DES-ECBencrypto bock size : 8md info :md type : 4md nid : 4md name : MD5md pkey type : 8md size : 16md block size : 64generate key value:54 0 b1 24 18 42 8d ddgenerate iv value:ba 7d c3 97 a0 c9 e0 70
3.3 示例3
#include <openssl/evp.h>#include <openssl/rsa.h>int main(){int ret,inlen,outlen=0;unsigned long e=RSA_3;char data[100],out[500];EVP_MD_CTX md_ctx,md_ctx2;EVP_PKEY *pkey;RSA *rkey;BIGNUM *bne;/* 待签名数据*/strcpy(data,"openssl 编程作者:赵春平");inlen=strlen(data);/* 生成RSA密钥*/bne=BN_new();ret=BN_set_word(bne,e);rkey=RSA_new();ret=RSA_generate_key_ex(rkey,1024,bne,NULL);if(ret!=1) goto err;pkey=EVP_PKEY_new();EVP_PKEY_assign_RSA(pkey,rkey);/* 初始化*/EVP_MD_CTX_init(&md_ctx);ret=EVP_SignInit_ex(&md_ctx,EVP_md5(), NULL);if(ret!=1)goto err;ret=EVP_SignUpdate(&md_ctx,data,inlen);if(ret!=1)goto err;ret=EVP_SignFinal(&md_ctx,out,&outlen,pkey);/* 验证签名*/EVP_MD_CTX_init(&md_ctx2);ret=EVP_VerifyInit_ex(&md_ctx2,EVP_md5(), NULL);if(ret!=1) goto err;ret=EVP_VerifyUpdate(&md_ctx2,data,inlen);if(ret!=1) goto err;ret=EVP_VerifyFinal(&md_ctx2,out,outlen,pkey);if(ret==1)printf("验证成功\n");elseprintf("验证错误\n");err:RSA_free(rkey);BN_free(bne);return 0;}
3.4 示例4
#include <openssl/evp.h>#include <openssl/rsa.h>int main(){int ret,ekl[2],npubk,inl,outl,total=0,total2=0;unsigned long e=RSA_3;char *ek[2],iv[8],in[100],out[500],de[500];EVP_CIPHER_CTX ctx,ctx2;EVP_CIPHER *type;EVP_PKEY *pubkey[2];RSA *rkey;BIGNUM *bne;/* 生成RSA密钥*/bne=BN_new();ret=BN_set_word(bne,e);rkey=RSA_new();ret=RSA_generate_key_ex(rkey,1024,bne,NULL);pubkey[0]=EVP_PKEY_new();EVP_PKEY_assign_RSA(pubkey[0],rkey);type=EVP_des_cbc();npubk=1;EVP_CIPHER_CTX_init(&ctx);ek[0]=malloc(500);ek[1]=malloc(500);ret=EVP_SealInit(&ctx,type,ek,ekl,iv,pubkey,1); /* 只有一个公钥*/if(ret!=1) goto err;strcpy(in,"openssl 编程");inl=strlen(in);ret=EVP_SealUpdate(&ctx,out,&outl,in,inl);if(ret!=1)goto err;total+=outl;ret=EVP_SealFinal(&ctx,out+outl,&outl);if(ret!=1) goto err;total+=outl;memset(de,0,500);EVP_CIPHER_CTX_init(&ctx2);ret=EVP_OpenInit(&ctx2,EVP_des_cbc(),ek[0],ekl[0],iv,pubkey[0]);if(ret!=1) goto err;ret=EVP_OpenUpdate(&ctx2,de,&outl,out,total);total2+=outl;ret=EVP_OpenFinal(&ctx2,de+outl,&outl);total2+=outl;de[total2]=0;printf("%s\n",de);err:free(ek[0]);free(ek[1]);EVP_PKEY_free(pubkey[0]);BN_free(bne);getchar();return 0;}
输出结果:openssl 编程