openssl+EVP详解


EVP(Enveloped Public Key)是 OpenSSL 中用于提供对称加密、非对称加密和哈希功能的高级加
密接口。EVP 库提供了一个抽象的加密框架,使得可以在不同的算法实现之间进行切换,而不需要改变应用程序的代码。以下是一些 EVP 开发的主要方面:

一、EVP基本介绍

1. EVP 加密和解密

EVP 提供了通用的加密和解密函数,可以用于对称加密和非对称加密。一般的流程如下:

  • 选择加密算法,创建相应的 EVP_CIPHER 结构。
  • 初始化 EVP_CIPHER_CTX 上下文。
  • 使用 EVP_EncryptInit_exEVP_DecryptInit_ex 初始化加密或解密操作。
  • 使用 EVP_EncryptUpdateEVP_DecryptUpdate 处理数据。
  • 使用 EVP_EncryptFinal_exEVP_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_MDEVP_PKEY 结构。
  • 初始化 EVP_MD_CTX 上下文。
  • 使用 EVP_DigestInit_ex 初始化哈希操作。
  • 使用 EVP_DigestUpdate 处理数据。
  • 使用 EVP_DigestFinal_ex 完成哈希操作。
  • 使用 EVP_SignInit_exEVP_VerifyInit_ex 初始化签名或验证操作。
  • 使用 EVP_SignUpdateEVP_VerifyUpdate 处理数据。
  • 使用 EVP_SignFinalEVP_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_exEVP_DecryptUpdateEVP_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();

       else

              cipher=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_ofb

2: EVP_des_ede3_cbc

1

加密结果长度:100

test ok!

please select :

1: EVP_des_ede3_ofb

2: EVP_des_ede3_cbc

2

加密结果长度:104

test 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 : 29

encrypto name: DES-ECB

encrypto bock size : 8

md info :

md type  : 4

md nid  : 4

md name : MD5

md pkey type : 8

md size : 16

md block size : 64

generate key value:

54 0 b1 24 18 42 8d dd

generate 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");

       else

              printf("验证错误\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 编程

  • 38
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用 OpenSSL 和 PKCS#11 进行加解密的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/evp.h> #include <openssl/err.h> #include <openssl/pkcs12.h> #include <openssl/engine.h> #include <openssl/pkcs11.h> #define BUFSIZE 1024 int main(int argc, char *argv[]) { int rv, i; char *infile, *outfile, *password; FILE *in, *out; EVP_CIPHER_CTX *ctx = NULL; PKCS12 *p12 = NULL; X509 *cert = NULL; EVP_PKEY *pkey = NULL; PKCS11_CTX *p11ctx = NULL; CK_OBJECT_HANDLE hkey; CK_SESSION_HANDLE hsession; if (argc != 4) { printf("Usage: %s <infile> <outfile> <password>\n", argv[0]); return 1; } infile = argv[1]; outfile = argv[2]; password = argv[3]; /* Initialize OpenSSL and PKCS#11 */ ERR_load_crypto_strings(); ENGINE_load_builtin_engines(); ENGINE_register_all_complete(); rv = PKCS11_CTX_init(&p11ctx, "pkcs11", NULL, NULL, 0); if (rv != CKR_OK) { printf("Error initializing PKCS#11 context\n"); goto cleanup; } /* Load PKCS#12 file */ in = fopen(infile, "rb"); if (in == NULL) { printf("Error opening input file: %s\n", infile); goto cleanup; } p12 = d2i_PKCS12_fp(in, NULL); if (p12 == NULL) { printf("Error reading PKCS#12 file: %s\n", infile); goto cleanup; } fclose(in); /* Extract certificate and private key from PKCS#12 file */ rv = PKCS12_parse(p12, password, &pkey, &cert, NULL); if (rv == 0) { printf("Error parsing PKCS#12 file: %s\n", infile); goto cleanup; } /* Find corresponding private key in PKCS#11 token */ rv = PKCS11_CTX_login(p11ctx, CKU_USER, password); if (rv != CKR_OK) { printf("Error logging in to PKCS#11 token\n"); goto cleanup; } hsession = PKCS11_CTX_get_session(p11ctx); rv = PKCS11_find_key(p11ctx, &hkey, cert, pkey); if (rv != CKR_OK) { printf("Error finding private key in PKCS#11 token\n"); goto cleanup; } /* Initialize context for decryption */ ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { printf("Error creating cipher context\n"); goto cleanup; } rv = EVP_OpenInit(ctx, EVP_aes_256_cbc(), NULL, 0, NULL, pkey); if (rv != 1) { printf("Error initializing cipher context\n"); goto cleanup; } /* Decrypt input file and write output file */ in = fopen(infile, "rb"); if (in == NULL) { printf("Error opening input file: %s\n", infile); goto cleanup; } out = fopen(outfile, "wb"); if (out == NULL) { printf("Error opening output file: %s\n", outfile); goto cleanup; } while (1) { unsigned char inbuf[BUFSIZE], outbuf[BUFSIZE]; int inlen, outlen; inlen = fread(inbuf, 1, BUFSIZE, in); if (inlen == 0) { break; } rv = EVP_OpenUpdate(ctx, outbuf, &outlen, inbuf, inlen); if (rv != 1) { printf("Error decrypting input file\n"); goto cleanup; } fwrite(outbuf, 1, outlen, out); } rv = EVP_OpenFinal(ctx, NULL, 0); if (rv != 1) { printf("Error finalizing cipher context\n"); goto cleanup; } fclose(in); fclose(out); printf("Decryption successful\n"); cleanup: if (ctx != NULL) { EVP_CIPHER_CTX_free(ctx); } if (p12 != NULL) { PKCS12_free(p12); } if (cert != NULL) { X509_free(cert); } if (pkey != NULL) { EVP_PKEY_free(pkey); } if (p11ctx != NULL) { PKCS11_CTX_logout(p11ctx); PKCS11_CTX_uninit(p11ctx); } ENGINE_cleanup(); ERR_free_strings(); return 0; } ``` 这是一个简单的解密示例,它可以从 PKCS#12 文件中提取证书和私钥,并使用 PKCS#11 模块从智能卡中获取私钥,然后使用 OpenSSL 中的 EVP 函数进行解密操作。你可以根据自己的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

N阶二进制

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值