EVP简介
Openssl EVP提供了丰富的密码学中的各种函数。
Openssl 中实现了各种对称算法、摘要算法以及签名/验签算法。 EVP 函数将这些具体的算法进行了封装。
EVP 主要封装了如下功能函数:
1)实现了 base64 编解码 BIO;
2)实现了加解密 BIO;
3)实现了摘要 BIO;
4)实现了 reliable BIO;
5)封装了摘要算法;
6)封装了对称加解密算法;
7)封装了非对称密钥的加密(公钥)、解密(私钥)、签名与验证以及辅助函数;
7)基于口令的加密(PBE);
8)对称密钥处理;
9)数字信封:数字信封用对方的公钥加密对称密钥,数据则用此对称密钥加密。发送给对方时,同时发送对称密钥密文和数据密文。接收方首先用自己的私钥解密密钥密文,得到对称密钥,然后用它解密数据。
10)其他辅助函数。
EVP相关数据结构
EVP相关数据结构
声明在文件openssl-1.1.0c\include\opensslossl_typ.h中
EVP相关算法上下文数据结构定义在文件openssl-1.1.0c\crypto\evp\evp_locl.h中。
EVP相关算法数据结构定义在文件openssl-1.1.0c\crypto\include\internal\evp_int.h中。
typedef struct evp_cipher_st EVP_CIPHER;
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
typedef struct evp_md_st EVP_MD;
typedef struct evp_md_ctx_st EVP_MD_CTX;
typedef struct evp_pkey_st EVP_PKEY;
typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD;
typedef struct evp_pkey_method_st EVP_PKEY_METHOD;
typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
typedef struct evp_Encode_Ctx_st EVP_ENCODE_CTX;
EVP_CIPHER
EVP_CIPHER用来存放对称加密相关的信息以及算法。
struct evp_cipher_st {
int nid; //对称算法 nid
int block_size; //对称算法每次加解密的字节数
/* Default value for variable length ciphers */
int key_len; //对称算法的密钥长度字节数
int iv_len; //对称算法的填充长度
/* Various flags */
unsigned long flags; //用于标记
/* init key */
/*加密初始化函数,用来初始化 ctx, key 为对称密钥值, iv 为初始化向量, enc用于指明是要加密还是解密,这些信息存放在 ctx 中*/
int (*init) (EVP_CIPHER_CTX *ctx, const unsigned char *key,
const unsigned char *iv, int enc);
/* encrypt/decrypt data */
/*对称运算函数,用于加密或解密*/
int (*do_cipher) (EVP_CIPHER_CTX *ctx, unsigned char *out,
const unsigned char *in, size_t inl);
/* cleanup ctx 清除上下文函数*/
int (*cleanup) (EVP_CIPHER_CTX *);
/* how big ctx->cipher_data needs to be */
int ctx_size;
/* Populate a ASN1_TYPE with parameters */
/*设置上下文参数函数*/
int (*set_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *);
/* Get parameters from a ASN1_TYPE */
/*获取上下文参数函数*/
int (*get_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *);
/* Miscellaneous operations 控制函数,实现各种其他操作*/
int (*ctrl) (EVP_CIPHER_CTX *, int type, int arg, void *ptr);
/* Application data 用于存放应用数据*/
void *app_data;
} /* EVP_CIPHER */ ;
对称算法上下文结构EVP_CIPHER_CTX ,此结构主要用来维护加解密状态,存放中间以及最后结果。因为加密或解密时,当数据很多时,可能会用到 Update 函数,并且每次加密或解密的输入数据长度任意的,并不一定是对称算法 block_size 的整数倍,所以需要用该结构来存放中间未加密的数据。
struct evp_cipher_ctx_st {
const EVP_CIPHER *cipher;
ENGINE *engine; /* functional reference if 'cipher' is
* ENGINE-provided */
int encrypt; /* encrypt or decrypt */
int buf_len; /* number we have left */
unsigned char oiv[EVP_MAX_IV_LENGTH]; /* original iv */
unsigned char iv[EVP_MAX_IV_LENGTH]; /* working iv */
unsigned char buf[EVP_MAX_BLOCK_LENGTH]; /* saved partial block */
int num; /* used by cfb/ofb/ctr mode */
/* FIXME: Should this even exist? It appears unused */
void *app_data; /* application stuff */
int key_len; /* May change for variable length cipher */
unsigned long flags; /* Various flags */
void *cipher_data; /* per EVP data */
int final_used;
int block_mask;
unsigned char final[EVP_MAX_BLOCK_LENGTH]; /* possible final block */
} /* EVP_CIPHER_CTX */ ;
openssl 对于各种对称算法实现了上述结构,各个源码位于 cypto/evp 目录下,文件名以 e_开头。 Openssl 通过这些结构来封装了对称算法相关的运算。
EVP_MD
EVP_MD结构用来存放摘要算法信息以及各种计算函数。
struct evp_md_st {
int type; //摘要类型,一般是摘要算法 NID
int pkey_type; //公钥类型,一般是签名算法 NID
int md_size; //摘要值大小,为字节数
unsigned long flags; //用于设置标记
/*摘要算法初始化函数*/
int (*init) (EVP_MD_CTX *ctx);
/*多次摘要函数*/
int (*update) (EVP_MD_CTX *ctx, const void *data, size_t 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 block_size;
int ctx_size; /* how big does the ctx->md_data need to be */
/* control function */
int (*md_ctrl) (EVP_MD_CTX *ctx, int cmd, int p1, void *p2);
} /* EVP_MD */ ;
struct evp_md_ctx_st {
const EVP_MD *digest;
/* functional reference if 'digest' is ENGINE-provided */
ENGINE *engine;
unsigned long flags;
void *md_data;
/* Public key context for sign/verify */
EVP_PKEY_CTX *pctx;
/* Update function: usually copied from EVP_MD */
int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
} /* EVP_MD_CTX */ ;
openssl 对于各种摘要算法实现了上述结构,各个源码位于 cypto/evp 目录下,文件名以 m_开头。 Openssl 通过这些结构来封装了各个摘要相关的运算。
EVP_PKEY
EVP_PKEY用来存放非对称密钥信息,可以是 RSA、 DSA、 DH 或 ECC 密钥。其中, ptr 用来存放密钥结构地址, attributes 堆栈用来存放密钥属性。
/*
* Type needs to be a bit field Sub-type needs to be for variations on the
* method, as in, can it do arbitrary encryption....
*/
struct evp_pkey_st {
int type;
int save_type;
int references;
const EVP_PKEY_ASN1_METHOD *ameth;
ENGINE *engine;
union {
void *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
# ifndef OPENSSL_NO_EC
struct ec_key_st *ec; /* ECC */
# endif
} pkey;
int save_parameters;
STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */
CRYPTO_RWLOCK *lock;
} /* EVP_PKEY */ ;
EVP源码结构
evp 源码位于 crypto/evp 目录,可以分为如下几类:
1) 全局函数
主要包括 c_allc.c、 c_alld.c 以及 names.c。他们加载 openssl 支持的所有的对称算法和摘要算法,放入到哈希表中。实现了 OpenSSL_add_all_digests、
OpenSSL_add_all_ciphers 以及 OpenSSL_add_all_algorithms(调用了前两个函数)函数。在进行计算时,用户也可以单独加载摘要函数( EVP_add_digest)和对称计算函数( EVP_add_cipher)。
2) BIO 扩充
包括 bio_b64.c、 bio_enc.c、 bio_md.c 和 bio_ok.c,各自实现了 BIO_METHOD方法,分别用于 base64 编解码、对称加解密以及摘要。
3) 摘要算法 EVP 封装
由 digest.c 实现,实现过程中调用了对应摘要算法的回调函数。各个摘要算法提供了自己的 EVP_MD 静态结构,对应源码为 m_xxx.c。
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 函数,该函数主要用于清除内存中的密钥信息。
5) 非对称算法 EVP 封装
主要是以 p_开头的文件。其中, p_enc.c 封装了公钥加密; p_dec.c 封装了私钥解密; p_lib.c 实现一些辅助函数; p_sign.c 封装了签名函数; p_verify.c 封装了验签函数; p_seal.c 封装了数字信封; p_open.c 封装了解数字信封。
6) 基于口令的加密
包括 p5_crpt2.c、 p5_crpt.c 和 evp_pbe.c。