最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

openssl的EVP函数详解

来源:动视网 责编:小OO 时间:2025-09-24 13:06:59
文档

openssl的EVP函数详解

openssl之EVP系列之1---算法封装    ---根据openssl doc\\crypto\\EVP.pod翻译和自己的理解写成    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之openssl专业论坛,版本:openssl-0.9.7)    EVP系列的函数定义包含在"evp.h"里面,这是一系列封装了openssl加密库里面所有算法的函数。通过这样的统一的封装,使得只需要在初始化参数的时候做很少
推荐度:
导读openssl之EVP系列之1---算法封装    ---根据openssl doc\\crypto\\EVP.pod翻译和自己的理解写成    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之openssl专业论坛,版本:openssl-0.9.7)    EVP系列的函数定义包含在"evp.h"里面,这是一系列封装了openssl加密库里面所有算法的函数。通过这样的统一的封装,使得只需要在初始化参数的时候做很少
openssl之EVP系列之1---算法封装

    ---根据openssl doc\\crypto\\EVP.pod翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    EVP系列的函数定义包含在"evp.h"里面,这是一系列封装了openssl加密库里面所有

算法的函数。通过这样的统一的封装,使得只需要在初始化参数的时候做很少的改变,

就可以使用相同的代码但采用不同的加密算法进行数据的加密和解密。

    EVP系列函数主要封装了三大类型的算法,要支持全部这些算法,请调用OpenSSL_a

dd_all_algorithms函数,下面分别就其结构作一个简单的介绍。

    【公开密钥算法】

    函数名称:EVP_Seal*...*,EVP_Open*...*

    功能描述:该系列函数封装提供了公开密钥算法的加密和解密功能,实现了电子信

封的功能。

    相关文件:p_seal.c,p_open.c

    【数字签名算法】

    函数名称:EVP_Sign*...*,EVP_Verify*...*

    功能描述:该系列函数封装提供了数字签名算法和功能。

    相关文件:p_sign.c,p_verify.c

    【对称加密算法】

    函数名称:EVP_Encrypt*...*

    功能描述:该系列函数封装提供了对称加密算法的功能。

    相关文件:evp_enc.c,p_enc.c,p_dec.c,e_*.c

    【信息摘要算法】

    函数名称:EVP_Digest*...*

    功能描述:该系列函数封装实现了多种信息摘要算法。

    相关文件:digest.c,m_*.c

    【信息编码算法】

    函数名称:EVP_Encode*...*

    功能描述:该系列函数封装实现了ASCII码与二进制码之间的转换函数和功能。

    相关文件:encode.c

    注意:

    自从出现engin版本以后,所有对称加密算法和摘要算法可以用ENGINE模块实现的算

法代替。如果ENGINE模块实现的对称加密和信息摘要函数被注册为缺省的实现算法,那

么当使用各种EVP函数时,软件编译的时候会自动将该实现模块连接进去。

openssl之EVP系列之2---对称加密算法概述

    ---根据openssl doc\\crypto\\EVP_EncryptInit.pod和doc\\ssleay.txt cipher.doc

部分翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    对称加密算法封装的函数系列名字是以EVP_Encrypt*...*开头的,其实,这些函数

只是简单调用了EVP_Cipher*...*系列的同名函数,换一个名字可能是为了更好的区别和

理解。除了实现了对称加密算法外,EVP_Encrypt*...*系列还对块加密算法提供了缓冲

功能。以后我们可能会更多使用EVP_Cipher的术语,因为它是真正的实现结构。

    EVP_Cipher*...*得以实现的一个基本结构是下面定义的一个算法结构,它定义了E

VP_Cipher系列函数应该采用什么算法进行数据处理,其定义如下(evp.h):

    typedef struct evp_cipher_st

    {

     int nid;

     int block_size;

     int key_len;

     int iv_len;

     unsigned long flags;

     int (*init)(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsign

ed char *iv, int enc);

     int (*do_cipher)(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigne

d char *in, unsigned int inl);

     int (*cleanup)(EVP_CIPHER_CTX *);

     int ctx_size;

     int (*set_asn1_parameters)(EVP_CIPHER_CTX *, ASN1_TYPE *);

     int (*get_asn1_parameters)(EVP_CIPHER_CTX *, ASN1_TYPE *);

     int (*ctrl)(EVP_CIPHER_CTX *, int type, int arg, void *ptr); /* Miscell

aneous operations */

     void *app_data;

    }EVP_CIPHER;

    下面对这个结构的部分成员的含义作一些解释:

    nid——是算法类型的nid识别号,openssl里面每个对象都有一个内部唯一的识别I

D

    block_size——是每次加密的数据块的长度,以字节为单位

    key_len——各种不同算法缺省的密钥长度

    iv_len——初始化向量的长度

    init——算法结构初始化函数,可以设置为加密模式还是解密模式

    do_cipher——进行数据加密或解密的函数

    cleanup——释放EVP_CIPHER_CTX结构里面的数据和设置。

    ctx_size——设定ctx->cipher_data数据的长度

    set_asn1_parameters——在EVP_CIPHER_CTX结构中通过参数设置一个ASN1_TYPE

    get_asn1_parameters——从一个ASN1_TYPE中取得参数

    ctrl——其它各种操作函数

    app_data——应用数据

    通过定义这样一个指向这个结构的指针,你就可以在连接程序的时候只连接自己使

用的算法;而如果你是通过一个整数来指明应该使用什么算法的话,会导致所有算法的

代码都被连接到代码中。通过这样一个结构,还可以自己增加新的算法。

    在这个基础上,每个EVP_Cipher*...*函数都维护着一个指向一个EVP_CIPHER_CTX结

构的指针。

    typedef struct evp_cipher_ctx_st

    {

     const EVP_CIPHER *cipher;

     ENGINE *engine;

     int encrypt;

     int buf_len;

     unsigned char oiv[EVP_MAX_IV_LENGTH];

     unsigned char iv[EVP_MAX_IV_LENGTH];

     unsigned char buf[EVP_MAX_BLOCK_LENGTH];

     int num;

     void *app_data;

     int key_len;

     unsigned long flags;

     void *cipher_data;

     int final_used;

     int block_mask;

     unsigned char final[EVP_MAX_BLOCK_LENGTH];

     } EVP_CIPHER_CTX;

    下面对这个结构部分成员做简单的解释:

    cipher——是该结构相关的一个EVP_CIPHER算法结构

    engine——如果加密算法是ENGINE提供的,那么该成员保存了相关的函数接口

    encrypt——加密或解密的标志

    buf_len——该结构缓冲区里面当前的数据长度

    oiv——初始的初始化向量

    iv——工作时候使用的初始化向量

    buf——保存下来的部分需要数据

    num——在cfb/ofb模式的时候指定块长度

    app_data——应用程序要处理数据

    key_len——密钥长度,算法不一样长度也不一样

    cipher_data——加密后的数据

    上述两个结构是EVP_Cipher(EVP_Encrypt)系列的两个基本结构,它们的其它一些列

函数都是以这两个结构为基础实现了。文件evp\\evp_enc.c是最高层的封装实现,各种加

密的算法的封装在p_enc.c里面实现,解密算法的封装在p_dec.c里面实现,而各个e_*.

c文件则是真正实现了各种算法的加解密功能,当然它们其实也是一些封装函数,真正的

算法实现在各个算法同名目录里面的文件实现。

openssl之EVP系列之3---EVP_Encrypt支持的对称加密算法列表

    ---根据openssl doc\\crypto\\EVP_EncryptInit.pod和doc\\ssleay.txt cipher.doc

部分翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    openssl对称加密算法的格式都以函数形式提供,其实该函数返回一个该算法的结构

体,其形式一般如下:

    EVP_CIPHER* EVP_*(void)

    在openssl中,所有提供的对称加密算法长度都是固定的,有特别说明的除外。下面

对这些算法进行分类的介绍,首先介绍一下算法中使用的通用标志的含义。

    【通用标志】

    ecb——电子密码本(Electronic Code Book)加密方式

    cbc——加密块链接(Cipher Block Chaining)加密方式

    cfb——位加密反馈(Cipher Feedback)加密方式

    ofb——位输出反馈(Output Feedback)加密方式

    ede——该加密算法采用了加密、解密、加密的方式,第一个密钥和最后一个密钥是

相同的

    ede3——该加密算法采用了加密、解密、加密的方式,但是三个密钥都不相同

    【NULL算法】

    函数:EVP_enc_null()

    说明:该算法不作任何事情,也就是没有进行加密处理

    【DES算法】

    函数:EVP_des_cbc(void), EVP_des_ecb(void), EVP_des_cfb(void), EVP_des_o

fb(void)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的DES算法

    【使用两个密钥的3DES算法】

    函数:EVP_des_ede_cbc(void), EVP_des_ede(), EVP_des_ede_ofb(void),EVP_de

s_ede_cfb(void)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的3DES算法,算法的第一个

密钥和最后一个密钥相同,事实上就只需要两个密钥

    【使用三个密钥的3DES算法】

    函数:EVP_des_ede3_cbc(void), EVP_des_ede3(), EVP_des_ede3_ofb(void), EV

P_des_ede3_cfb(void)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的3DES算法,算法的三个密

钥都不相同

    【DESX算法】

    函数:EVP_desx_cbc(void)

    说明:CBC方式DESX算法

    【RC4算法】

    函数:EVP_rc4(void)

    说明:RC4流加密算法。该算法的密钥长度可以改变,缺省是128位。

    【40位RC4算法】

    函数:EVP_rc4_40(void)

    说明:密钥长度40位的RC4流加密算法。该函数可以使用EVP_rc4和EVP_CIPHER_CTX

_set_key_length函数代替。

    【IDEA算法】

    函数:EVP_idea_cbc(),EVP_idea_ecb(void), EVP_idea_cfb(void), EVP_idea_o

fb(void)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的IDEA算法。

    【RC2算法】

    函数:EVP_rc2_cbc(void), EVP_rc2_ecb(void), EVP_rc2_cfb(void), EVP_rc2_o

fb(void)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的RC2算法,该算法的密钥长

度是可变的,可以通过设置有效密钥长度或有效密钥位来设置参数来改变。缺省的是12

8位。

    【定长的两种RC2算法】

    函数:EVP_rc2_40_cbc(void), EVP_rc2__cbc(void)

    说明:分别是40位和位CBC模式的RC2算法。

    【Blowfish算法】

    函数:EVP_bf_cbc(void), EVP_bf_ecb(void), EVP_bf_cfb(void), EVP_bf_ofb(v

oid)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的Blowfish算法,该算法的

密钥长度是可变的

    【CAST算法】

    函数:EVP_cast5_cbc(void), EVP_cast5_ecb(void), EVP_cast5_cfb(void), EVP

_cast5_ofb(void)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的CAST算法,该算法的密钥

长度是可变的

    【RC5算法】

    函数:EVP_rc5_32_12_16_cbc(void), EVP_rc5_32_12_16_ecb(void), EVP_rc5_32

_12_16_cfb(void), EVP_rc5_32_12_16_ofb(void)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的RC5算法,该算法的密钥长

度可以根据参数“number of rounds”(算法中一个数据块被加密的次数)来设置,缺

省的是128位密钥,加密次数为12次。目前来说,由于RC5算法本身实现代码的,加

密次数只能设置为8、12或16。

    【128位AES算法】

    函数:EVP_aes_128_ecb(void),EVP_aes_128_cbc(void),PEVP_aes_128_cfb(voi

d),EVP_aes_128_ofb(void)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的128位AES算法

    【192位AES算法】

    函数:EVP_aes_192_ecb(void),EVP_aes_192_cbc(void),PEVP_aes_192_cfb(voi

d),EVP_aes_192_ofb(void)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的192位AES算法

    【256位AES算法】

    函数:EVP_aes_256_ecb(void),EVP_aes_256_cbc(void),PEVP_aes_256_cfb(voi

d),EVP_aes_256_ofb(void)

    说明:分别是CBC方式、ECB方式、CFB方式以及OFB方式的256位AES算法

    上述的算法是0.9.7版本支持的所有对称加密算法,关于算法的详细情况,请参看该

算法的资料了或本系列后续的文章。

openssl之EVP系列之4---EVP_Encrypt系列函数详解(一)

    ---根据openssl doc\\crypto\\EVP_EncryptInit.pod和doc\\ssleay.txt cipher.doc

部分翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    EVP_Cipher系列包含了很多函数,我将他们大概分成两部分来介绍,一部分是基本

函数系列,就是本文要介绍的,另一个部分是设置函数系列,将在后面的文章进行介绍

。基本系列函数主要是进行基本的加密和解密操作的函数,他们的定义如下(openssl\

evp.h):

     int EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a);

     int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,

     ENGINE *impl, unsigned char *key, unsigned char *iv);

     int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,

     int *outl, unsigned char *in, int inl);

     int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out,

     int *outl);

     int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,

     ENGINE *impl, unsigned char *key, unsigned char *iv);

     int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,

     int *outl, unsigned char *in, int inl);

     int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm,

     int *outl);

     int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,

     ENGINE *impl, unsigned char *key, unsigned char *iv, int enc);

     int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,

     int *outl, unsigned char *in, int inl);

     int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm,

     int *outl);

     int EVP_EncryptInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,

     unsigned char *key, unsigned char *iv);

     int EVP_EncryptFinal(EVP_CIPHER_CTX *ctx, unsigned char *out,

     int *outl);

     int EVP_DecryptInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,

     unsigned char *key, unsigned char *iv);

     int EVP_DecryptFinal(EVP_CIPHER_CTX *ctx, unsigned char *outm,

     int *outl);

     int EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,

     unsigned char *key, unsigned char *iv, int enc);

     int EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *outm,

     int *outl);

     int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a);

    其实在这里列出的函数虽然很多,但是大部分是功能重复的,有的是旧的版本支持

的函数,新的版本中已经可以不再使用了。事实上,函数EVP_EncryptInit, EVP_Encry

ptFinal, EVP_DecryptInit, EVP_CipherInit以及EVP_CipherFinal在新代码中不应该继

续使用,他们保留下来只是为了兼容以前的代码。在新的代码中,应该使用EVP_Encryp

tInit_ex、EVP_EncryptFinal_ex、EVP_DecryptInit_ex、EVP_DecryptFinal_ex、EVP_

CipherInit_ex以及EVP_CipherFinal_ex函数,因为它们可以在每次调用完算法后,不用

重新释放和分配已有EVP_CIPHER_CTX结构的内存的情况下重用该结构,方便很多。下面

我们分别对这些函数进行介绍。

    【EVP_CIPHER_CTX_init】

    该函数初始化一个EVP_CIPHER_CTX结构体,只有初始化后该结构体才能在下面介绍

的函数中使用。操作成功返回1,否则返回0。

    【EVP_EncryptInit_ex】

    该函数采用ENGINE参数impl的算法来设置并初始化加密结构体。其中,参数ctx必须

在调用本函数之前已经进行了初始化。参数type通常通过函数类型来提供参数,如EVP_

des_cbc函数的形式,即我们上一章中介绍的对称加密算法的类型。如果参数impl为NUL

L,那么就会使用缺省的实现算法。参数key是用来加密的对称密钥,iv参数是初始化向

量(如果需要的话)。在算法中真正使用的密钥长度和初始化密钥长度是根据算法来决

定的。在调用该函数进行初始化的时候,除了参数type之外,所有其它参数可以设置为

NULL,留到以后调用其它函数的时候再提供,这时候参数type就设置为NULL就可以了。

在缺省的加密参数不合适的时候,可以这样处理。操作成功返回1,否则返回0。

    【EVP_EncryptUpdate】

    该函数执行对数据的加密。该函数加密从参数in输入的长度为inl的数据,并将加密

好的数据写入到参数out里面去。可以通过反复调用该函数来处理一个连续的数据块。写

入到out的数据数量是由已经加密的数据的对齐关系决定的,理论上来说,从0到(inl+c

ipher_block_size-1)的任何一个数字都有可能(单位是字节),所以输出的参数out要

有足够的空间存储数据。写入到out中的实际数据长度保存在outl参数中。操作成功返回

1,否则返回0。

    【EVP_EncryptFinal_ex】

    该函数处理最后(Final)的一段数据。在函数在padding功能打开的时候(缺省)

才有效,这时候,它将剩余的最后的所有数据进行加密处理。该算法使用标志的块padd

ing方式(AKA PKCS padding)。加密后的数据写入到参数out里面,参数out的长度至少

应该能够一个加密块。写入的数据长度信息输入到outl参数里面。该函数调用后,表示

所有数据都加密完了,不应该再调用EVP_EncryptUpdate函数。如果没有设置padding功

能,那么本函数不会加密任何数据,如果还有剩余的数据,那么就会返回错误信息,也

就是说,这时候数据总长度不是块长度的整数倍。操作成功返回1,否则返回0。

    PKCS padding标准是这样定义的,在被加密的数据后面加上n个值为n的字节,使得

加密后的数据长度为加密块长度的整数倍。无论在什么情况下,都是要加上padding的,

也就是说,如果被加密的数据已经是块长度的整数倍,那么这时候n就应该等于块长度。

比如,如果块长度是9,要加密的数据长度是11,那么5个值为5的字节就应该增加在数据

的后面。

    【EVP_DecryptInit_ex, EVP_DecryptUpdate和EVP_DecryptFinal_ex】

    这三个函数是上面三个函数相应的解密函数。这些函数的参数要求基本上都跟上面

相应的加密函数相同。如果padding功能打开了,EVP_DecryptFinal会检测最后一段数据

的格式,如果格式不正确,该函数会返回错误代码。此外,如果打开了padding功能,E

VP_DecryptUpdate函数的参数out的长度应该至少为(inl+cipher_block_size)字节;

但是,如果加密块的长度为1,则其长度为inl字节就足够了。三个函数都是操作成功返

回1,否则返回0。

    需要注意的是,虽然在padding功能开启的情况下,解密操作提供了错误检测功能,

但是该功能并不能检测输入的数据或密钥是否正确,所以即便一个随机的数据块也可能

无错的完成该函数的调用。如果padding功能关闭了,那么当解密数据长度是块长度的整

数倍时,操作总是返回成功的结果。

    【EVP_CipherInit_ex, EVP_CipherUpdate和EVP_CipherFinal_ex】

    事实上,上面介绍的函数都是调用这三个函数实现的,它们是更底层的函数。完成

了数据的加密和解密功能。他们根据参数enc决定执行加密还是解密操作,如果enc为1,

则加密;如果enc为0,则解密;如果enc是-1,则不改变数据。三个函数都是操作成功

返回1,否则返回0。

    【EVP_CIPHER_CTX_cleanup】

    该函数清除一个EVP_CIPHER_CTX结构中的所有信息并释放该结构占用的所有内存。

在使用上述的函数完成一个加密算法过程后应该调用该函数,这样可以避免一些敏感信

息遗留在内存造成安全隐犯。操作成功返回1,否则返回0。

    【EVP_EncryptInit, EVP_DecryptInit和EVP_CipherInit】

    这三个函数的功能分别跟函数EVP_EncryptInit_ex, EVP_DecryptInit_ex和EVP_Ci

pherInit_ex功能相同,只是他们的ctx参数不需要进行初始化,并且使用缺省的算法库

。三个函数都是操作成功返回1,否则返回0。

    【EVP_EncryptFinal, EVP_DecryptFinal和EVP_CipherFinal】

    这三个函数分别跟函数EVP_EncryptFinal_ex, EVP_DecryptFinal_ex以及EVP_Ciph

erFinal_ex函数功能相同,不过,他们的参数ctx会在调用后自动释放。三个函数都是操

作成功返回1,否则返回0。

openssl之EVP系列之5---EVP_Encrypt系列函数详解(二)

    ---根据openssl doc\\crypto\\EVP_EncryptInit.pod和doc\\ssleay.txt cipher.doc

部分翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    前面的文章我们介绍了EVP_ENcrypt系列函数的基本部分,本文将介绍他们的一些扩

充部分,即一些参数设置和其它辅助的函数,其定义如下(openssl\\evp.h):

     int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *x, int padding);

     int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen);

     const EVP_CIPHER *EVP_get_cipherbyname(const char *name);

     #define EVP_get_cipherbynid(a) EVP_get_cipherbyname(OBJ_nid2sn(a))

     #define EVP_get_cipherbyobj(a) EVP_get_cipherbynid(OBJ_obj2nid(a))

     int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a);

     #define EVP_CIPHER_nid(e) ((e)->nid)

     #define EVP_CIPHER_block_size(e) ((e)->block_size)

     #define EVP_CIPHER_key_length(e) ((e)->key_len)

     #define EVP_CIPHER_iv_length(e) ((e)->iv_len)

     #define EVP_CIPHER_flags(e) ((e)->flags)

     #define EVP_CIPHER_mode(e) ((e)->flags) & EVP_CIPH_MODE)

     int EVP_CIPHER_type(const EVP_CIPHER *ctx);

     #define EVP_CIPHER_CTX_cipher(e) ((e)->cipher)

     #define EVP_CIPHER_CTX_nid(e) ((e)->cipher->nid)

     #define EVP_CIPHER_CTX_block_size(e) ((e)->cipher->block_size)

     #define EVP_CIPHER_CTX_key_length(e) ((e)->key_len)

     #define EVP_CIPHER_CTX_iv_length(e) ((e)->cipher->iv_len)

     #define EVP_CIPHER_CTX_get_app_data(e) ((e)->app_data)

     #define EVP_CIPHER_CTX_set_app_data(e,d) ((e)->app_data=(char *)(d))

     #define EVP_CIPHER_CTX_type(c) EVP_CIPHER_type(EVP_CIPHER_CTX_cipher(c)

)

     #define EVP_CIPHER_CTX_flags(e) ((e)->cipher->flags)

     #define EVP_CIPHER_CTX_mode(e) ((e)->cipher->flags & EVP_CIPH_MODE)

     int EVP_CIPHER_param_to_asn1(EVP_CIPHER_CTX *c, ASN1_TYPE *type);

     int EVP_CIPHER_asn1_to_param(EVP_CIPHER_CTX *c, ASN1_TYPE *type);

    【EVP_CIPHER_CTX_set_padding】

    该函数设置是否采用padding功能.在算法缺省的情况下,是使用标准的块padding功

能的,并且在解密的时候会自动接测padding并将它删除。如果将参数pad设置为0,则p

adding功能就会被禁止,那么在加密和解密的时候,数据应该为加密块长度的整数倍,

否则就会出错。函数恒返回1。

    【EVP_CIPHER_CTX_set_key_length】

    该函数进行加密算法结构EVP_CIPHER_CTX密钥长度的设置。如果算法是一个密钥长

度固定的算法,那么如果设置的密钥长度跟它固定的长度不一致,就会产生错误。

    【EVP_get_cipherbyname, EVP_get_cipherbynid和EVP_get_cipherbyobj】

    这三个函数都根据给定的参数返回一个EVP_CIPHER结构,不同的是给定的参数分别

是算法名称、算法的NID和一个ASN1_OBJECT结构。具体的算法名称、NID以及ASN1_OBJE

CT结构请参看object\\boject.h文件的定义。

    【EVP_CIPHER_nid和EVP_CIPHER_CTX_nid】

    这两个函数返回EVP_CIPHER或EVP_CIPHER_CTX结构内部的算法的NID。返回的NID值

只是一个内部存储的值,并不一定真的有相应的OBJECT定义。

    【EVP_CIPHER_key_length和EVP_CIPHER_CTX_key_length】

    这两个函数返回EVP_CIPHER或EVP_CIPHER_CTX结构内部的算法的密钥长度。常量EV

P_MAX_KEY_LENGTH定义了所有算法最长的密钥长度。需要注意的是,对于EVP_CIPHER_k

ey_length函数来说,对特定的一种算法密钥长度是不变的,但是EVP_CIPHER_CTX_key_

length函数对同一个算法密钥长度却是可变的。

    【EVP_CIPHER_iv_length和EVP_CIPHER_CTX_iv_length】

    这两个函数返回EVP_CIPHER或EVP_CIPHER_CTX结构内部的算法的初始化向量长度。

如果算法不使用IV,那么就会返回0。常量EVP_MAX_IV_LENGTH定义了所有算法最长的IV

长度

    【EVP_CIPHER_block_size和EVP_CIPHER_CTX_block_size】

    这两个函数返回EVP_CIPHER或EVP_CIPHER_CTX结构内部的算法的加密块长度。常量

EVP_MAX_IV_LENGTH也是所有算法最长的块长度。

    【EVP_CIPHER_type和EVP_CIPHER_CTX_type】

    这两个函数返回EVP_CIPHER或EVP_CIPHER_CTX结构内部的算法的类型。该类型的值

是算法的NID,一般来说,NID忽略了算法的一些参数,如40位和129位RC2算法的NID是相

同的。如果算法没有相应定义的NID或者不是ASN1所支持的,那么本函数就会返回NID_u

ndef。

    【EVP_CIPHER_CTX_cipher】

    该函数返回EVP_CIPHER_CTX结构里面的EVP_CIPHER结构。

    【EVP_CIPHER_mode和EVP_CIPHER_CTX_mode】

    这两个函数返回相应结构算法的块加密模式,包括EVP_CIPH_ECB_MODE, EVP_CIPH_

CBC_MODE, EVP_CIPH_CFB_MODE和EVP_CIPH_OFB_MODE;如果算法是流加密算法,那么就

返回EVP_CIPH_STREAM_CIPHER 。

    【EVP_CIPHER_param_to_asn1】

    该函数设置算法结构的参数,一般来说设置的值包括了所有参数和一个IV值。如果

算法有IV,那么调用该函数时IV是必须设置的。该函数必须在所设置的算法结构使用之

前(如调用EVP_EncryptUpdate和EVP_DecryptUpdate函数之前)调用。如果ASN1不支持

该算法,那么调用该函数将导致失败。操作成功返回1,否则返回0。

    【EVP_CIPHER_asn1_to_param】

    该函数给用算法结构里面的值设置参数type的结构。其设置的内容由具体的算法决

定。如在RC2算法中,它会设置IV和有效密钥长度。本函数应该在算法结构的基本算法类

型已经设置了但是密钥还没有设置之前调用。例如,调用EVP_CipherInit函数的时候使

用参数IV,并将key设置位NULL,然后就应该调用本函数,最后再调用EVP_CipherInit,

这时候除了key设置位NULL外所有参数都应该设置。当ASN1不支持不支持该算法或者有参

数不能设置的时候(如RC2的有效密钥长度不支持),该函数调用就会失败。操作成功返

回1,否则返回0。

    【EVP_CIPHER_CTX_ctrl】

    该函数可以设置不同算法的特定的参数。目前只有RC2算法的有效密钥长度和RC5算

法的加密次数(rounds)可以进行设置。

    BTW:我自己感觉都写的有一点慢了,知道大家想知道怎么用来编程,但是,先把这

么多函数介绍清楚了,下面看起来就会轻松多了,下一篇就将介绍EVP_Encrypt*...*系

列函数的编程框架,并举几个例子。

openssl之EVP系列之6---EVP_Encrypt系列函数编程架构及例子

    ---根据openssl doc\\crypto\\EVP_EncryptInit.pod和doc\\ssleay.txt cipher.doc

部分翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    在前面的两篇文章,已经对EVP_Encrypt*...*系列函数做了详细的介绍,本章将说

明该系列函数通用的应用架构,并举几个函数应用例子。

    【应用架构】

    一般来说,EVP_Encrypt*...*系列函数的应用架构如下所描述(假设加密算法为3DE

S):

    1.定义一些必须的变量

    char key[EVP_MAX_KEY_LENGTH];

    char iv[EVP_MAX_IV_LENGTH];

    EVP_CIPHER_CTX ctx;

    unsigned char out[512+8];

    int outl;

    2.给变量key和iv赋值,这里使用了函数EVP_BytesToKey,该函数从输入密码产生了

密钥key和初始化向量iv,该函数将在后面做介绍。如果可以有别的办法设定key和iv,

该函数的调用不是必须的

    EVP_BytesToKey(EVP_des_ede3_cbc,EVP_md5,NULL,passwd,strlen(passwd),key,i

v);

    3.初始加密算法结构EVP_CIPHER_CTX

    EVP_EncryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, key, iv);

    4.进行数据的加密操作

    while (....)

    {

     EVP_EncryptUpdate(ctx,out,&outl,in,512);

    }

    一般来说采用了循环的结构进行处理,每次循环加密数据为512字节,密文输出到o

ut,out和int应该是指向不相同的内存的。

    5.结束加密,输出最后的一段512字节的数据

    EVP_EncryptFinal_ex(&ctx, out, &outl)

    该函数会进行加密的检测,如果加密过程有误,一般会检查出来。

    说明:加密跟上述过程是一样的,只不过要使用EVP_Decrypt*...*系列函数。

    【例子】

    1.取得算法RC5使用的循环次数(round)

     int nrounds;

     EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GET_RC5_ROUNDS, 0, &nrounds);

    2.取得算法RC2的有效密钥长度

     int key_bits;

     EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GET_RC2_KEY_BITS, 0, &key_bits);

    3.设置算法RC5使用的循环次数(round)

     int nrounds;

     EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC5_ROUNDS, nrounds, NULL);

    4.设置算法RC2的有效密钥长度

     int key_bits;

     EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, key_bits, NULL);

    5.使用Blowfish算法加密一个字符串

     int do_crypt(char *outfile)

     {

     unsigned char outbuf[1024];

     int outlen, tmplen;

     //其实key和iv一般应该从别的地方得到,这里固定了至少作为演示使用的

     unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

     unsigned char iv[] = {1,2,3,4,5,6,7,8};

     char intext[] = "Some Crypto Text";

     EVP_CIPHER_CTX ctx;

     FILE *out;

     EVP_CIPHER_CTX_init(&ctx);

     EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, key, iv);

     if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, strlen(intext)))

     {

     /* 出错处理*/

     return 0;

     }

     //注意,传入给下面函数的输出缓存参数必须注意不能覆盖了原来的加密输出的数

     if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen))

     {

     /* 出错处理*/

     return 0;

     }

     outlen += tmplen;

     EVP_CIPHER_CTX_cleanup(&ctx);

     //注意,保存到文件的时候要使用二进制模式打开文件,因为密文数据是二进制的

,而且,不能采用strlen函数,因为密文字符串不是以NULL(0)为结束的字符串

     out = fopen(outfile, "wb");

     fwrite(outbuf, 1, outlen, out);

     fclose(out);

     return 1;

     }

    上面举的例子加密的密文可以使用openssl提供的应用程序cipher.exe来解密,命令

如下:

     openssl bf -in cipher.bin -K 000102030405060708090A0B0C0D0E0F -iv 01020

30405060708 -d

    6.使用文件I/O和80位密钥RC2算法的通用加密解密函数实例,该函数可以进行加密

,也可以进行解密,由参数do_encrypt决定,该参数为1时则执行加密,为0时则执行解

密。

     int do_crypt(FILE *in, FILE *out, int do_encrypt)

     {

     /*为输出缓冲设置足够的附加空间*/

     inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];

     int inlen, outlen;

     //其实key和iv一般应该从别的地方得到,这里固定了至少作为演示使用的

     unsigned char key[] = "01234567";

     unsigned char iv[] = "12345678";

     //这时候不进行key和IV的设置,因为我们还要修改参数

     EVP_CIPHER_CTX_init(&ctx);

     EVP_CipherInit_ex(&ctx, EVP_rc2(), NULL, NULL, NULL, do_encrypt);

     EVP_CIPHER_CTX_set_key_length(&ctx, 10);

     //完成参数设置,进行key和IV的设置

     EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, do_encrypt);

     for(;;)

     {

     inlen = fread(inbuf, 1, 1024, in);

     if(inlen <= 0) break;

     if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))

     {

     /*出错处理 */

     return 0;

     }

     fwrite(outbuf, 1, outlen, out);

     }

     if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))

     {

     /* 出错处理*/

     return 0;

     }

     fwrite(outbuf, 1, outlen, out);

     EVP_CIPHER_CTX_cleanup(&ctx);

     return 1;

     }

openssl之EVP系列之7---信息摘要算法结构概述

    ---根据openssl doc\\crypto\\EVP_DigestInit.pod翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    该系列函数封装了openssl加密库所有的信息摘要算法,通过这种EVP封装,当使用

不同的信息摘要算法时,只需要对初始化参数修改一下就可以了,其它代码可以完全一

样。这些算法包括MD2、MD5以及SHA等算法。

    【EVP_MD结构介绍】

    所有的算法都维护着下面定义的结构的一个指针,在此基础上实现了算法的功能。

该结构EVP_MD如下:

    typedef struct env_md_st

    {

     int type;

     int pkey_type;

     int md_size;

     unsigned long flags;

     int (*init)(EVP_MD_CTX *ctx);

     int (*update)(EVP_MD_CTX *ctx,const void *data,unsigned long count);

     int (*final)(EVP_MD_CTX *ctx,unsigned char *md);

     int (*copy)(EVP_MD_CTX *to,const EVP_MD_CTX *from);

     int (*cleanup)(EVP_MD_CTX *ctx);

     int (*sign)();

     int (*verify)();

     int required_pkey_type[5]; /*EVP_PKEY_xxx */

     int block_size;

     int ctx_size;

    } EVP_MD;

    下面对该结构体的部分参数解释:

    type——信息摘要算法的NID标识

    pkey_type——是信息摘要-签名算法的相应NID标识,如NID_shaWithRSAEncry

ption

    md_size——是信息摘要算法生成的信息摘要的长度,如SHA算法是SHA_DIGEST_LEN

GTH,该值是20

    init——指向一个特定信息摘要算法的初始化函数,如对于SHA算法,指针指向SHA

_Init

    update——指向一个真正计算摘要值的函数,例如SHA算法就是指向SHA_Update

    final——指向一个信息摘要值计算之后要调用的函数,该函数完成最后的一块数据

的处理工作。例如SHA算法就是指向SHA_Final.

    copy——指向一个可以在两个EVP_MD_CTX结构之间拷贝参数值的函数

    required_pkey_type——指向一个用来签名的算法EVP_PKEY的类型,如SHA算法就指

向EVP_PKEY_RSA_method

    block_size——一个用来进行信息摘要的输入块的的长度(单位是字节),如SHA算

法就是SHA_CBLOCK

    ctx_size——是CTX结构的长度,在SHA算法里面应该就是sizeof(EVP_MD*)+sizeof

(SHA_CTX)

    如果你要增加新的算法,那么可以定义这个结构,并进行必要的一直,然后就可以

使用通用的函数了。跟EVP_CIPHER系列函数一样,使用这个封装技术,就可以在使用一

种摘要算法时,比如MD5,在连接程序的时候就只连接MD5的代码。如果使用证书来标识

算法,那么就会导致所有其它的信息摘要算法代码都连接到程序中去了。

    【EVP_MD_CTX结构介绍】

    在调用函数的时候,一般来说需要传入上面说的type的参数和下面所定义的一个CT

X结构,该结构EVP_MD_CTX定义如下:

    typedef struct env_md_ctx_st

    {

     const EVP_MD *digest;

     ENGINE *engine;

     unsigned long flags;

     void *md_data;

    }EVP_MD_CTX ;

    该结构的成员解释如下:

    digest——指向上面介绍的EVP_MD结构的指针

    engine——如果算法由ENGINE提供,该指针指向该ENGINE

    md_data——信息摘要数据

    【支持的信息摘要算法】

    EVP_md_null(void)

    EVP_md2(void)

    EVP_md4(void)

    EVP_md5(void)

    EVP_sha(void)

    EVP_sha1(void)

    EVP_dss(void)

    EVP_dss1(void)

    EVP_mdc2(void)

    EVP_ripemd160(void)

openssl之EVP系列之8---EVP_Digest系列函数详解

    ---根据openssl doc\\crypto\\EVP_DigestInit.pod翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    EVP_Digest系列提供了与EVP_Encrypt系列相似的函数,定义如下(openssl/evp.h

):

     void EVP_MD_CTX_init(EVP_MD_CTX *ctx);

     EVP_MD_CTX *EVP_MD_CTX_create(void);

     int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl

);

     int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt);

     int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md,

     unsigned int *s);

     int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx);

     void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx);

     int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out,const EVP_MD_CTX *in);

     int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type);

     int EVP_DigestFinal(EVP_MD_CTX *ctx, unsigned char *md,

     unsigned int *s);

     int EVP_MD_CTX_copy(EVP_MD_CTX *out,EVP_MD_CTX *in);

     #define EVP_MAX_MD_SIZE (16+20) /* The SSLv3 md5+sha1 type */

     #define EVP_MD_type(e) ((e)->type)

     #define EVP_MD_pkey_type(e) ((e)->pkey_type)

     #define EVP_MD_size(e) ((e)->md_size)

     #define EVP_MD_block_size(e) ((e)->block_size)

     #define EVP_MD_CTX_md(e) (e)->digest)

     #define EVP_MD_CTX_size(e) EVP_MD_size((e)->digest)

     #define EVP_MD_CTX_block_size(e) EVP_MD_block_size((e)->digest)

     #define EVP_MD_CTX_type(e) EVP_MD_type((e)->digest)

    【EVP_MD_CTX_init】

    该函数初始化一个EVP_MD_CTX结构。

    【EVP_MD_CTX_create】

    该函数创建一个EVP_MD_CTX结构,分配内存并进行初始化,返回该结构。

    【EVP_DigestInit_ex】

    该函数使用参数impl所指向的ENGINE设置该信息摘要结构体,参数ctx在调用本函数

之前必须经过初始化。参数type一般是使用象EVP_sha1这样的函数的返回值。如果impl

为NULL,那么就会使用缺省实现的信息摘要函数。大多数应用程序里面impl是设置为NU

LL的。操作成功返回1,否则返回0。

    【EVP_DigestUpdate】

    该函数将参数d中的cnt字节数据进行信息摘要到ctx结构中去,该函数可以被调用多

次,用以对更多的数据进行信息摘要。操作成功返回1,否则返回0。

    【EVP_DigestFinal_ex】

    本函数将ctx结构中的摘要信息数据返回到参数md中,如果参数s不是NULL,那么摘

要数据的长度(字节)就会被写入到参数s中,大多数情况瞎,写入的值是EVP_MAX_MD_

SIZE。在调用本函数后,不能使用相同的ctx结构调用EVP_DigestUpdate再进行数据的信

息摘要操作,但是如果调用EVP_DigestInit_ex函数重新初始化后可以进行新的信息摘要

操作。操作成功返回1,否则返回0。

    【EVP_MD_CTX_cleanup】

    清除一个信息摘要结构,该函数应该在一个信息摘要结构使用后不再需要的时候调

用。

    【EVP_MD_CTX_destroy】

    清除信息摘要结构并释放所有分配的内存空间,只有使用EVP_MD_CTX_create函数创

建的信息摘要结构才能使用该函数进行释放。

    【EVP_MD_CTX_copy_ex】

    该函数可以用来将信息摘要数据从in结构拷贝到out结构中。如果有大量的数据需要

进行信息摘要,而且这些数据只有最后几个字节不同的时候,使用该函数就显得特别有

用,节省时间。其中,out结构必须在调用本函数之前进行初始化。操作成功返回1,否

则返回0。

    【EVP_DigestInit】

    该函数功能跟EVP_DigestInit_ex函数相同,但是ctx参数可以不用初始化,而且该

函数只使用缺省实现的算法。

    【EVP_DigestFinal】

    该函数功能跟EVP_DigestFinal_ex函数相同,但是ctx结构会自动清除。一般来说,

现在新的程序应该使用EVP_DigestInit_ex和EVP_DigestFinal_ex函数,因为这些函数可

以在使用完一个EVP_MD_CTX结构后,不用重新声明和初始化该结构就能使用它进行新的

数据处理,而且新的带_ex的函数也可以使用非缺省的实现算法库。

    【EVP_MD_CTX_copy】

    该函数跟EVP_MD_CTX_copy_ex函数功能相同,但是out参数可以不用初始化。

    【EVP_MD_size和EVP_MD_CTX_size】

    这两个函数返回结构里面摘要信息的长度。

    【EVP_MD_block_size和EVP_MD_CTX_block_size】

    这两个函数返回摘要信息块处理的长度。

    【EVP_MD_type和EVP_MD_CTX_type】

    这两个函数返回信息摘要结构算法的NID。例如,EVP_MD_type(EVP_sha1())返回NI

D_sha1。该函数通常在设置ASN1 OID的时候使用。如果算法不存在,返回NID_undef。

    【EVP_MD_CTX_md】

    该函数返回给定EVP_MD_CTX结构里面的EVP_MD结构。

    【EVP_MD_pkey_type】

    该函数返回信息摘要结构里面公钥签名算法的NID。例如,如果EVP_sha1是使用RSA

签名算法,那么就会返回NID_sha1WithRSAEncryption。

    【EVP_md2、EVP_md5、EVP_sha、EVP_sha1、EVP_mdc2和EVP_ripemd160】

    这些函数返回相应名字的EVP_MD结构,它们都使用RSA算法作为签名算法。在新的程

序里,一般推荐使用sha1算法。

    【EVP_dss和EVP_dss1】

    这两个函数返回的EVP_MD结构分别使用sha和sha1信息摘要算法,但是签名算法使用

DSS(DSA)。

    【EVP_md_null】

    该函数返回的信息摘要结构不作任何事情,返回的摘要信息长度为0。

    【EVP_get_digestbyname、EVP_get_digestbynid和EVP_get_digestbyobj】

    这三个函数分别根据给定的算法名称、算法NID以及ASN1_OBJECT结构返回一个相应

的EVP_MD算法结构。摘要算法在使用之前必须进行初始化,如使用Openssl_add_all_di

gests进行初始化。如果调用不成功,返回NULL。

openssl之EVP系列之9---EVP_Digest系列函数的一个例子

    ---根据openssl doc\\crypto\\EVP_DigestInit.pod翻译

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    本例子是openssl帮助文档提供的。该例子根据命令行输入的信息摘要算法名字对"

Test Message\\n"和"Hello World\\n"字符串进行信息摘要操作。

     #include 

     #include 

     main(int argc, char *argv[])

     {

     EVP_MD_CTX mdctx;

     const EVP_MD *md;

     char mess1[] = "Test Message\\n";

     char mess2[] = "Hello World\\n";

     unsigned char md_value[EVP_MAX_MD_SIZE];

     int md_len, i;

     //使EVP_Digest系列函数支持所有有效的信息摘要算法

     OpenSSL_add_all_digests();

     if(!argv[1]) {

     printf("Usage: mdtest digestname\\n");

     exit(1);

     }

     //根据输入的信息摘要函数的名字得到相应的EVP_MD算法结构

     md = EVP_get_digestbyname(argv[1]);

     if(!md) {

     printf("Unknown message digest %s\\n", argv[1]);

     exit(1);

     }

     //初始化信息摘要结构mdctx,这在调用EVP_DigestInit_ex函数的时候是必须的。

     EVP_MD_CTX_init(&mdctx);

     //使用md的算法结构设置mdctx结构,impl为NULL,即使用缺省实现的算法(open

ssl本身提供的信息摘要算法)

     EVP_DigestInit_ex(&mdctx, md, NULL);

     //开始真正进行信息摘要运算,可以多次调用该函数,处理更多的数据,这里只调

用了两次

     EVP_DigestUpdate(&mdctx, mess1, strlen(mess1));

     EVP_DigestUpdate(&mdctx, mess2, strlen(mess2));

     //完成信息摘要计算过程,将完成的摘要信息存储在md_value里面,长度信息存储

在md_len里面

     EVP_DigestFinal_ex(&mdctx, md_value, &md_len);

     //使用该函数释放mdctx占用的资源,如果使用_ex系列函数,这是必须调用的。

     EVP_MD_CTX_cleanup(&mdctx);

     printf("Digest is: ");

     for(i = 0; i < md_len; i++) printf("%02x", md_value[i]);

     printf("\\n");

     }

openssl之EVP系列之10---EVP_Sign系列函数介绍

    ---根据openssl doc\\crypto\\EVP_SignInit.pod翻译

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    EVP_Sign系列函数使用的基础结构跟信息摘要算法使用的基础结构是一样的,而且

,其前面的两个操作步骤初始化和数据操作(信息摘要)也跟信息摘要算法是一样的,

唯一不一样的是最后一步操作,本系列函数做了签名的工作,而信息摘要系列函数当然

就只是简单的处理完摘要信息了事了。其实这是很容易理解的事情,因为签名算法就是

在信息摘要之后用私钥进行签名的过程。本系列函数定义的如下(openssl\\evp.h):

     int EVP_SignInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl);

     int EVP_SignUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt);

     int EVP_SignFinal(EVP_MD_CTX *ctx,unsigned char *sig,unsigned int *s, E

VP_PKEY *pkey);

     void EVP_SignInit(EVP_MD_CTX *ctx, const EVP_MD *type);

     int EVP_PKEY_size(EVP_PKEY *pkey);

    【EVP_SignInit_ex】

    该函数是一个宏定义函数,其实际定义如下:

    #define EVP_SignInit_ex(a,b,c) EVP_DigestInit_ex(a,b,c)

    可见,该函数跟前面叙述的EVP_DigestInit_ex的功能和使用方法是一样的,都是使

用ENGINE参数impl所代表的实现函数功能来设置结构ctx。在调用本函数前,参数ctx一

定要经过EVP_MD_CTX_init函数初始化。详细使用方法参看前面的文章介绍。成功返回1

,失败返回0。

    【EVP_SignUpdate】

    该函数也是一个宏定义函数,其实际定义如下:

    #define EVP_SignUpdate(a,b,c) EVP_DigestUpdate(a,b,c)

    该函数使用方法和功能也跟前面介绍的EVP_DigestUpdate函数一样,将一个cnt字节

的数据经过信息摘要运算存储到结构ctx中,该函数可以在一个相同的ctx中调用多次来

实现对更多数据的信息摘要工作。成功返回1,失败返回0。

    【EVP_SignFinal】

    该函数跟前面两个函数不同,这是签名系列函数跟信息摘要函数开始不同的地方,

其实,该函数是将签名操作的信息摘要结构ctx拷贝一份,然后调用EVP_DigestFinal_e

x完成信息摘要工作,然后开始对摘要信息用私钥pkey进行签名,并将签名信息保存在参

数sig里面。如果参数s不为NULL,那么就会将签名信息数据的长度(单位字节)保存在

该参数中,通常写入的数据是EVP_PKEY_size(key)。

    因为操作的时候是拷贝了一份ctx,所以,原来的ctx结构还可以继续使用EVP_Sign

Update和EVP_SignFinal函数来完成更多信息的签名工作。不过,最后一定要使用EVP_M

D_CTX_cleanup函数清除和释放ctx结构,否则就会造成内存泄漏。

    此外,当使用DSA私钥签名的时候,一定要对产生的随机数进行种子播种工作(see

ded),否则操作就会失败。RSA算法则不一定需要这样做。至于使用的签名算法跟摘要算

法的关系,在EVP_Digest系列中已经有详细说明,这里不再重复。

    本函数操作成功返回1,否则返回0。

    【EVP_SignInit】

    本函数也是一个宏定义函数,其定义如下:

    #define EVP_SignInit(a,b) EVP_DigestInit(a,b)

    所以其功能和用法跟前面介绍的EVP_DigestInit函数完全一样,使用缺省实现的算

法初始化算法结构ctx。

    【EVP_PKEY_size】

    本函数返回一个签名信息的最大长度(单位字节)。实际签名信息的长度则由上述

的函数EVP_SignFinal返回,有可能比这小。

    上述所有函数发生错误,可以使用ERR_get_error函数获得错误码。

openssl之EVP系列之11---EVP_Verify系列函数介绍

    ---根据openssl doc\\crypto\\EVP_VerifyInit.pod翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    跟EVP_Sign系列函数一样,EVP_Verify系列函数的前两步(初始化和信息摘要处理

)跟信息摘要算法是一样的,因为签名验证的过程就是先对信息进行信息摘要,然后再

将发来的摘要信息解密后进行比较的过程,其定义如下(openssl\\evp.h):

     int EVP_VerifyInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl

);

     int EVP_VerifyUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt);

     int EVP_VerifyFinal(EVP_MD_CTX *ctx,unsigned char *sigbuf, unsigned int

 siglen,EVP_PKEY *pkey);

     int EVP_VerifyInit(EVP_MD_CTX *ctx, const EVP_MD *type);

    【EVP_VerifyInit_ex】

    该函数是一个宏定义函数,其实际定义如下:

    #define EVP_VerifyInit_ex(a,b,c) EVP_DigestInit_ex(a,b,c)

    所以,其功能和使用方法跟前面介绍的EVP_DigestInit_ex函数是一样的。该函数使

用参数impl所提供的算法库对验证结构ctx进行设置。在调用本函数之前,参数ctx必须

经过调用EVP_MD_CTX_init进行初始化。成功返回1,失败返回0。

    【EVP_VerifyUpdate】

    该函数也是一个宏定义函数,其实际定义如下:

    #define EVP_VerifyUpdate(a,b,c) EVP_DigestUpdate(a,b,c)

    所以,其功能和使用方法跟前面介绍的EVP_DigestUpdate函数是相同的。该函数将

参数d中的cnt字节数据经过信息摘要计算后保存到ctx中,该函数可以进行多次调用,以

处理更多的数据。成功调用返回1,失败返回0。

    【EVP_VerifyFinal】

    该函数使用公钥pkey和ctx结构里面的信息验证sigbuf里面的数据的签名。事实上,

该函数先调用EVP_MD_CTX_copy_ex函数将原来的ctx拷贝一份,然后调用EVP_DigestFin

al_ex函数完成拷贝的ctx的信息摘要计算,最后才使用公钥进行签名的验证工作。

    因为该函数实际上处理的是原来ctx函数的一个拷贝,所以原来的ctx结构还可以调

用EVP_VerifyUpdate和EVP_VerifyFinal函数进行更多的数据处理和签名验证工作。

    在使用完之后,ctx必须使用EVP_MD_CTX_cleanup函数释放内存,否则就会导致内存

泄漏。

    此外,至于信息摘要算法和签名算法的关联的关系,请参照信息摘要算法部分的说

明。

    该函数调用成功返回1,失败则返回0或-1。

    【EVP_VerifyInit】

    该函数使用缺省的实现算法对ctx结构进行初始化。也是一个宏定义函数,其定义如

下:

    #define EVP_VerifyInit(a,b) EVP_DigestInit(a,b)

    所以跟EVP_DigestInit函数功能和用法是一样的。

openssl之EVP系列之12---EVP_Seal系列函数介绍

    ---根据openssl doc\\crypto\\EVP_SealInit.pod翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    改系列函数是相当于完成一个电子信封的功能,它产生一个随机密钥,然后使用一

个公钥对改密钥进行封装,数据可以使用随机密钥进行加密。

    信封加密在进行大量数据传输的时候是必须经常要用到的,因为公开密钥算法的加

解密速度很慢,但对称算法就快多了。所以一般用公开密钥算法进行加密密钥的传输,

而真正进行数据加密则使用对称加密算法。

    其定义的函数如下(openssl\\evp.h):

     int EVP_SealInit(EVP_CIPHER_CTX *ctx, EVP_CIPHER *type, unsigned char *

*ek,

     int *ekl, unsigned char *iv,EVP_PKEY **pubk, int npubk);

     int EVP_SealUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,

     int *outl, unsigned char *in, int inl);

     int EVP_SealFinal(EVP_CIPHER_CTX *ctx, unsigned char *out,

     int *outl);

    【EVP_SealInit】

    该函数初始化一个加密算法结构EVP_CIPHER_CTX,采用了指定的加密算法,使用一

个随机密钥和初始化向量IV。事实上,该函数调用EVP_EncryptInit_ex函数两次完成了

ctx结构的初始化工作。参数type是算法类型,跟签名介绍过的是一样的,为EVP_des_c

bc类型的函数。随机私钥被一个或多个公钥加密,这就允许秘钥被公钥相应的私钥解密

。参数ek是一个缓存序列,可以存放多个被公钥加密后的密钥的信息,所以每个缓存空

间都应该足够大,比如ek[i]的缓存空间就必须为EVP_PKEY_size(pubk[i])那么大。每个

被加密密钥的长度保存在数字ekl中。参数pubk是一个公钥陈列,可以包含多个公钥。函

数成功执行返回npubk,失败返回0。

    因为该函数的密钥是随机产生的,随意在调用该函数之前,必须对随机数播种(se

eded)。

    使用的公钥必须是RSA,因为在openssl里面这是唯一支持密钥传输的公钥算法。

    跟EVP_EncryptInit函数一样,本函数也可以分为两次调用,第一次调用的时候要将

参数npubk设为0,第二调用的时候就应该将参数type设为NULL。

    【EVP_SealUpdate】

    该函数是一个宏定义函数,其实际定义如下:

    #define EVP_SealUpdate(a,b,c,d,e) EVP_EncryptUpdate(a,b,c,d,e)

    由此可见,其完成的功能和使用方法跟EVP_EncryptUpdate函数是一样的。细节参看

前面介绍的文章。成功执行返回1,否则返回0。

    【EVP_SealFinal】

    该函数简单调用了EVP_EncryptFinal_ex完成其功能,所以其完成的功能和使用参数

也跟EVP_EncryptFinal_ex函数一样,细节请参考相关文章。唯一不一样的是,该函数还

调用EVP_EncryptInit_ex(ctx,NULL,NULL,NULL,NULL)函数对ctx结构再次进行了初始化

。成功返回1,否则返回0。

openssl之EVP系列之13---EVP_Open系列函数介绍

    ---根据openssl doc\\crypto\\EVP_OpenInit.pod翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    本系列函数相对于EVP_Seal系列函数,是进行信封加密的。它将公钥加密了的密钥

加密出来,然后进行数据的解密。其定义的函数如下(openssl\\evp.h):

     int EVP_OpenInit(EVP_CIPHER_CTX *ctx,EVP_CIPHER *type,unsigned char *ek

,

     int ekl,unsigned char *iv,EVP_PKEY *priv);

     int EVP_OpenUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,

     int *outl, unsigned char *in, int inl);

     int EVP_OpenFinal(EVP_CIPHER_CTX *ctx, unsigned char *out,

     int *outl);

    【EVP_OpenInit】

    该函数初始化一个用来加密数据的ctx结构。它使用参数priv的私钥解密参数ek里面

长度为ekl字节的加密密钥。参数iv是初始化向量。如果参数type设定的加密算法长度是

可变的,那么密钥长度就会被设置为解密得到的密钥的长度;如果加密算法长度是固定

的,那么得到的解密密钥的长度就必须跟固定算法长度相同才行。成功执行返回密钥的

长度,否则返回0。

    跟函数EVP_DecryptInit一样,该函数也可以分成多次调用,首次调用应该将参数p

riv设置为NULL,再次调用的时候应该将type设置为NULL。

    【EVP_OpenUpdate】

    该函数是一个宏定义函数,其实际定义如下:

    #define EVP_OpenUpdate(a,b,c,d,e) EVP_DecryptUpdate(a,b,c,d,e)

    所以,其功能和使用方法跟前面介绍过的EVP_DecryptUpdate相同,请参考相应的文

章。成功执行返回1,否则返回0。

    【EVP_OpenFinal】

    事实上,该函数调用EVP_DecryptFinal_ex完成了其功能,所以其使用方法跟功能跟

函数EVP_DecryptFinal_ex是一样的,参考该函数说明就可以。唯一不同的是,本函数还

调用EVP_DecryptInit_ex(ctx,NULL,NULL,NULL,NULL)再次进行了初始化工作。成功执行

返回1,否则返回0。

openssl之EVP系列之14---EVP_Encode系列函数介绍

    ---根据自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    该系列函数主要对数据进行BASE编码,为此,它定义了一个简单的EVP_ENCODE_C

TX结构类型,由于该结构比较简单,主要包括了长度信息以及数据缓存,大家可以参考

evp.h,这里不再做介绍。

    EVP_Encode系列函数定义如下(openssl\\evp.h):

     void EVP_EncodeInit(EVP_ENCODE_CTX *ctx);

     void EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx,unsigned char *out,int *outl,

unsigned char *in,int inl);

     void EVP_EncodeFinal(EVP_ENCODE_CTX *ctx,unsigned char *out,int *outl);

     int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int n);

    【EVP_EncodeInit】

    该函数初始化一个用来进行base编码的结构,事实上,该函数只是简单设置了结

构里面几个常量的长度。

    【EVP_EncodeUpdate】

    该函数将参数in里面的inl自己数据拷贝到结构体ctx里面,如果结构体里面有数据

,就同时将结构体里面的数据进行BASE编码并输出到参数out指向的缓存里面,输出数

据的长度保存在outl里面。注意,在第一次调用本函数的时候,虽然往结构体里面拷贝

数据了,但是结构体ctx里面开始是没有输入数据存在并且输入数据长度不超出ctx内部

存储数据的最长,就不会有任何数据被进行BASE编码,也就是说,不会有任何数

据输出;但是如果输入数据长度比内部存储的数据长,那么就会输出部分经过BASE编

码的数据。数据输出总是在下一层输入前完成的。

    【EVP_EncodeFinal】

    该函数将结构体ctx里面剩余数据进行BASE编码并写入到参数out里面去,输出数

据的长度保存在outl里面。

    【EVP_EncodeBlock】

    该函数将参数f里面的字符串里面的n个字节的字符串进行BASE编码并输出到参数

t里面。返回数据的字节长度。事实上,在函数EVP_EncodeUpdate和EVP_EncodeFinal里

面就调用了该函数完成BASE编码功能。

openssl之EVP系列之15---EVP_Decode系列函数介绍

    ---根据自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    本系列函数与EVP_Encode系列函数相对,对数据进行BASE解码。其定义的函数如

下(openssl\\evp.h):

     void EVP_DecodeInit(EVP_ENCODE_CTX *ctx);

     int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx,unsigned char *out,int *outl,u

nsigned char *in, int inl);

     int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl)

;

     int EVP_DecodeBlock(unsigned char *t, const unsigned char *f, int n);

    【EVP_DecodeInit】

    该函数初始化一个用来进行BASE解码的数据结构。

    【EVP_DecodeUpdate】

    该函数将参数in里面inl字节的数据拷贝到结构体ctx里面。如果结构体里面已经有

数据,那么这些数据就会先进行BASE解码,然后输出到参数out指向的内存中,输出的

字节数保存在参数outl里面。输入数据为满行的数据时,返回为1;如果输入数据是最后

一行数据的时候,返回0;返回-1则表明出错了。

    【EVP_DecodeFinal】

    该函数将结构体ctx里面剩余的数据进行BASE解码并输出到参数out指向的内存中

,输出数据长度为outl字节。成功返回1,否则返回-1。

    【EVP_DecodeBlock】

    该函数将字符串f中的n字节数据进行BASE解码,并输出到out指向的内存中,输出

数据长度为outl。成功返回解码的数据长度,返回返回-1。

openssl之EVP系列之16---EVP_PKEY系列函数详解(-)

    ---根据自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    EVP_PKEY系列函数定义了一个密钥管理的结构体,其定义如下(openssl\\evp.h):

    typedef struct evp_pkey_st

    {

     int type;

     int save_type;

     int references;

     union

     {

     char *ptr;

     #ifndef OPENSSL_NO_RSA

     struct rsa_st *rsa; /* RSA */

     #endif

     #ifndef OPENSSL_NO_DSA

     struct dsa_st *dsa; /* DSA */

     #endif

     #ifndef OPENSSL_NO_DH

     struct dh_st *dh; /* DH */

     #endif

     } pkey;

     int save_parameters;

     STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */

    }EVP_PKEY;

    该密钥结构既可以用来存储公钥,也可以用来存储私钥。该结构其实是一个在open

ssl中很通用的结构,在许多算法中使用了该结构作为存储体。基于该结构定义的EVP_P

KEY系列函数有如下几个常用的:

     EVP_PKEY * EVP_PKEY_new(void);

     void EVP_PKEY_free(EVP_PKEY *pkey);

     int EVP_PKEY_type(int type);

     int EVP_PKEY_bits(EVP_PKEY *pkey);

     int EVP_PKEY_size(EVP_PKEY *pkey);

    这些函数的实现主要在文件evp\\p_lib.c里面,大家如果要看源代码,就可以看该文

件了。下面对这些函数分别进行简单的介绍。

    【EVP_PKEY_new】

    本函数生成并返回一个EVP_PKEY结构体,并对该结构体元素进行初始化,如果成功

调用返回该结构体的指针,否则返回NULL。因为该函数返回的只是一个空结构体,所以

还要使用下面的一些相关函数进行设置,如EVP_PKEY_set1_RSA之类的函数。

    【EVP_PKEY_free】

    该函数用来释放EVP_PKEY结构里面指针指向的内存空间。

    【EVP_PKEY_type】

    该函数返回输入结构密钥的类型,目前之支持RSA(EVP_PKEY_RSA)、DSA(EVP_PK

EY_DSA)以及DH(EVP_PKEY_DH)类型的密钥,如果为其它类型,则返回NID_undef。典

型使用方式是EVP_PKEY_type(pkey->type)。

    【EVP_PKEY_size】

    该函数返回EVP_PKEY结构中密钥的字节长度,只对RSA和DSA类型的密钥有效,如果

输入的参数为NULL或其它密钥类型,则返回0。

    【EVP_PKEY_bits】

    该函数返回EVP_PKEY结构中密钥的比特长度,只对RSA和DSA类型的密钥有效,如果

输入的参数没有初始化或其它密钥类型,则返回0。

openssl之EVP系列之17---EVP_PKEY系列函数详解(二)

    ---根据自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    接着前面介绍的EVP_PKEY系列函数,我们这章介绍的函数如下(openssl\\evp.h):

     int EVP_PKEY_assign(EVP_PKEY *pkey,int type,char *key);

     int EVP_PKEY_assign_RSA(EVP_PKEY *pkey,RSA *key);

     int EVP_PKEY_assign_DSA(EVP_PKEY *pkey,DSA *key);

     int EVP_PKEY_assign_DH(EVP_PKEY *pkey,DH *key);

     int EVP_PKEY_set1_RSA(EVP_PKEY *pkey,RSA *key);

     int EVP_PKEY_set1_DSA(EVP_PKEY *pkey,DSA *key);

     int EVP_PKEY_set1_DH(EVP_PKEY *pkey,DH *key);

     RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey);

     DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey);

     DH *EVP_PKEY_get1_DH(EVP_PKEY *pkey);

     EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey); int EVP_PKEY_copy_paramet

ers(EVP_PKEY *to,EVP_PKEY *from);

     int EVP_PKEY_missing_parameters(EVP_PKEY *pkey);

     int EVP_PKEY_save_parameters(EVP_PKEY *pkey,int mode);

     int EVP_PKEY_cmp_parameters(EVP_PKEY *a,EVP_PKEY *b);

     int EVP_PKEY_decrypt(unsigned char *dec_key,unsigned char *enc_key,int 

enc_key_len,EVP_PKEY *private_key);

     int EVP_PKEY_encrypt(unsigned char *enc_key,unsigned char *key,int key_

len,EVP_PKEY *pub_key);

    【EVP_PKEY_assign】

    该函数通过类型参数type和密钥值key给初始化了的EVP_PKEY结构pkey设置相应的参数,参数key的内存是直接跟pkey结构相关在一起的,所以key的内存在pkey使用期间不能改变,pkey被释放的时候,key也会被自动释放。当pkey不为NULL的时候,返回1,否则返回0。

    【EVP_PKEY_assign_RSA、EVP_PKEY_assign_DSA和EVP_PKEY_assign_DH】

    这三个函数都是上面的函数EVP_PKEY_assign函数的宏定义函数,分别完成RSA、DS

A以及DH类型密钥的pkey结构初始工作。当pkey不为NULL的时候,返回1,否则返回0。

    【EVP_PKEY_set1_RSA、EVP_PKEY_set1_DSA和EVP_PKEY_set1_DH】

    这三个函数跟上面函数的功能是一样的,只不过用这些函数设置的pkey释放的时候

,key内存不会被释放。openssl其实在内部做了一中类似于COM接口的引用技术,计算k

ey被引用的次数,从而实现了虽然公用内存,但是却能保持内存不被释放的功能。当pk

ey不为NULL的时候,返回1,否则返回0。

    【EVP_PKEY_get1_RSA、EVP_PKEY_get1_DSA和EVP_PKEY_get1_DH】

    这三个函数返回pkey结构相应类型的密钥指针,如果pkey结构里面的密钥类型跟函

数类型不一致,则返回NULL。

    【EVP_PKEY_copy_parameters】

    该函数将参数from结构体的参数拷贝到参数to结构体中,如果两个结构体的密钥类

型不一样,则不会拷贝任何东西,产生错误提示,返回0;如果from中没有参数,则产生

错误提示返回0。成功执行返回1。该函数只对DSA类型的密钥有效,其它类型密钥不会执

行任何操作。

    【EVP_PKEY_missing_parameters】

    如果结构pkey中的参数 p、q或g中有一个没有设置,那么该函数就会返回1,否则就

返回0。本函数只对DSA类型密钥有效。其它类型密钥都返回0。

    【EVP_PKEY_save_parameters】

    该函数也只对DSA类型密钥有效,如果密钥为DSA类型,那么参数pkey->save_param

eters就会被设置为参数mode的值。如果要重用该密钥的参数,mode应该设置为1。唯一

用到本函数功能的好象就是X509_PUBKEY_set函数。成功调用返回pkey参数原来的保存标

志,其它类型密钥则返回0。

    【EVP_PKEY_cmp_parameters】

    该函数也仅仅对DSA类型密钥有效,如果两个密钥的参数p、q和g都相同,则返回1,

否则返回0。如果两个密钥之中有一个或两个都不为DSA密钥,则返回-1。

    【EVP_PKEY_encrypt】

    该函数使用公钥pubkey将密钥key加密并保存在enc_key里面,返回输出值的长度。

enc_key分配的内存必须保证能够容下输出的数据。key参数的长度又参数key_len指定。

本函数在EVP_SealInit中被调用了,并且在PEM_SealInit中也被间接调用了。如果公钥

类型不是RSA,那么本函数返回-1。

    【EVP_PKEY_decrypt】

    跟上述函数相对应,该函数将参数ek里面的加密密钥用私钥priv解密并输出到参数

key中,返回输出结果的长度。key的长度也必须足够大,能够容下输出结果。ek的长度

由参数ekl指定。如果私钥不是RSA类型,那么本函数返回-1。

    【说明】

    此外,ASN1还提供了下列4个函数,在这里不再介绍,等到介绍ASN1函数再介绍这些

函数。

     EVP_PKEY * d2i_PublicKey(int type,EVP_PKEY **a, unsigned char **pp,long

 length);

     int i2d_PublicKey(EVP_PKEY *a, unsigned char **pp);

     EVP_PKEY * d2i_PrivateKey(int type,EVP_PKEY **a, unsigned char **pp,lon

g length);

     EVP_PKEY * d2i_AutoPrivateKey(EVP_PKEY **a, unsigned char **pp, long le

ngth);

     int i2d_PrivateKey(EVP_PKEY *a, unsigned char **pp);

openssl之EVP系列之18---EVP_BytesToKey函数介绍

    ---根据doc\\crypto\\EVP_BytesToKey.pod翻译和自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    该函数定义如下(openssl\\evp.h):

     int EVP_BytesToKey(const EVP_CIPHER *type,const EVP_MD *md,const unsign

ed char *salt, const unsigned char *data,int datal, int count, unsigned char

 *key,unsigned char *iv);

    该函数实现的功能是通过密码口令产生一个加密密钥和初始化向量IV。参数types是

要使用该密钥和IV的加密算法,类型位EVP_CIPHER。参数md是要使用的信息摘要算法。

参数salt是在提取密钥和IV的时候要使用的盐值,它应该指向一个8字节的缓存或者NUL

L,如果是NULL,就不使用盐值。参数data是包含了datal个字节数据的缓存,这些数据

用来提取密钥数据。参数count指定了迭代的次数。提取出来的密钥和IV向量分别保存在

参数key和iv里面。函数返回生成的密钥的长度。

    本函数使用的提取密钥算法是将产生的D_1,D_2,...,D_i,...等数据串连起来,一直

计算到数据长度满足了密钥和IV的长度。其中D_i定义如下:

     D_i = HASH^count(D_(i-1) || data || salt)

    这里的符号||表示数据串连,也就是连接起来。初始的D_0是空的,也就是说长度为

0。HASH是代表使用的信息摘要算法,HASH^1(data)就是HASH(data),HASH^2(data)是H

ASH(HASH(data)),其它以此类推。串连出来的数据前面的用来做密钥,后面的用来做IV

值。

    该函数一个典型的应用就是根据参数data中的口令来为一个加密算法产生加密密钥

。增加参数count会使算法的执行速度下降,但是却使得使用大量备选密码强力破解的黑

客行为难度大大增加。

    如果密钥和IV的长度比摘要算法的长度短并且使用了MD5算法,那么该函数使用的算

法就跟PKCS#5 v1.5标志是兼容的。否则,就会使用非标准的扩展方法来产生其它数据。

新的应用程序建议应该使用更标准的算法如PKCS#5 v2.0来提取加密密钥。

openssl之EVP系列之19---EVP提供对口令管理的函数介绍

    ---根据自己的理解写成

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    EVP提供的对口令管理的函数有三个,定义如下(openssl\\evp.h):

     int EVP_read_pw_string(char *buf,int length,const char *prompt,int veri

fy);

     void EVP_set_pw_prompt(char *prompt);

     char * EVP_get_pw_prompt(void);

    【EVP_read_pw_string】

    该函数从标准输入终端读入一个口令数据,其中,读入的参数保存在buf里面,参数

的长度保存在length里面。prompt参数里面保存的是输出到显示终端的提示语句;如果

verify参数设置为1,那么口令就会被要求输入两次,如果两次输入相同,才会成功返回

0,否则就会返回错误1,返回-1表示发生了系统错误。

    值得注意的是,由于历史原因,在openssl里面,该函数的核心代码是在DES算法里

面实现的,所以如果将DES算法禁止了,该函数的调用也会失败。

    【EVP_set_pw_prompt】

    该函数设置在调用EVP_read_pw_string函数的时候缺省的prompt参数的值,也就是

说如果调用EVP_read_pw_string函数的时候prompt参数为NULL,那就会使用本函数设置

的缺省的值。需要注意的是,本函数设置的缺省值其实是保存在一个静态的数组变量里

面,所以如果在Win16平台使用的时候,可能会出现莫名其妙的现象;在使用多线程程序

的时候也必须小心。

    【EVP_get_pw_prompt】

    该函数返回一个指向缺省prompt字符串(一个静态变量)的指针,如果该参数没有

设置,那么就返回NULL。

openssl之EVP系列之20---结束语

    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之

openssl专业论坛,版本:openssl-0.9.7)

    openssl提供的EVP系列函数,是对openssl所有各种加密算法做了一个高层的封装,

包括了以下几种:

    1.对称加密算法(EVP_Encrypt)

    2.信息摘要(HASH)算法(EVP_Digest)

    3.签名算法(EVP_Sign和EVP_Verify)

    4.公开密钥算法(EVP_Seal和EVP_Open)

    5.BASE编码算法(EVP_Encode和EVP_Decode)

    此外,为了提供更加完善的功能,以便实际应用的需要,openssl的EVP系列还提供

了以下几种辅助功能:

    1.不同类型密钥结构的管理(EVP_PKEY)

    2.加密密钥和IV值提取功能(EVP_BytesToKey)

    3.口令获取和管理功能

    如果能够对EVP系列函数有一个透彻的了解,那么我们基本上就能的心应手地使用o

penssl提供的加密函数的功能了。

    EVP系列的介绍已经完成,其中肯定有不少错误和不清楚的地方,希望大家指针。

文档

openssl的EVP函数详解

openssl之EVP系列之1---算法封装    ---根据openssl doc\\crypto\\EVP.pod翻译和自己的理解写成    (作者:DragonKing, Mail: wzhah@263.net ,发布于:http://openssl.126.com之openssl专业论坛,版本:openssl-0.9.7)    EVP系列的函数定义包含在"evp.h"里面,这是一系列封装了openssl加密库里面所有算法的函数。通过这样的统一的封装,使得只需要在初始化参数的时候做很少
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top