EVP_PKEY 以及 RSA 的区别

来源:互联网 发布:阿里云计算大会 编辑:程序博客网 时间:2024/06/05 04:32
刚学openssl的时候,有许多不明白之处。安装以及其他应用,见其他博文。
在使用RSA的过程中,使用了RSA定义的诸多接口。下面罗列一下。

#include <openssl/rsa.h>
 #include <openssl/engine.h>
 RSA * RSA_new(void);
 void RSA_free(RSA *rsa);
//公钥加密
 int RSA_public_encrypt(int flen, unsigned char *from,
    unsigned char *to, RSA *rsa, int padding);
//私钥解密
 int RSA_private_decrypt(int flen, unsigned char *from,
    unsigned char *to, RSA *rsa, int padding);
//私钥加密
 int RSA_private_encrypt(int flen, unsigned char *from,
    unsigned char *to, RSA *rsa,int padding);
//公钥解密
 int RSA_public_decrypt(int flen, unsigned char *from,
    unsigned char *to, RSA *rsa,int padding);
//签名
 int RSA_sign(int type, unsigned char *m, unsigned int m_len,
    unsigned char *sigret, unsigned int *siglen, RSA *rsa);
 //认证
int RSA_verify(int type, unsigned char *m, unsigned int m_len,
    unsigned char *sigbuf, unsigned int siglen, RSA *rsa);
//生成
 RSA *RSA_generate_key(int num, unsigned long e,
    void (*callback)(int,int,void *), void *cb_arg);
//新版
int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);

关于RSA公钥私钥,可以将其存储在文件中,称为PEM,可以读取和存储。下面是一些常用接口。
 RSA *PEM_read_RSAPublicKey(FILE *fp, RSA **x,
                                        pem_password_cb *cb, void *u);
 int PEM_write_RSAPublicKey(FILE *fp, RSA *x);
 RSA *PEM_read_RSA_PUBKEY(FILE *fp, RSA **x,
                                        pem_password_cb *cb, void *u);
 int PEM_write_RSA_PUBKEY(FILE *fp, RSA *x);

EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x,
                                        pem_password_cb *cb, void *u);
EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x,
pem_password_cb *cb, void *u);
int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
unsigned char *kstr, int klen,
pem_password_cb *cb, void *u);
int PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
unsigned char *kstr, int klen,
pem_password_cb *cb, void *u);


        思考一下,为什么openssl既然有了PEM文件的RSA接口,又要定义EVP_PKEY接口呢?为什么既要有BIO方式存取,也要有FILE方式存取呢?经过分析,其实这个不难理解。首先,openssl包含了许多非对称加密算法,每种算法都可以定义自己存取接口。同时,openssl定义了一种统一的接口方式,那就是EVP_PKEY存取接口,这种结构体可以保存各种不同的加密结构体,那样的话,就可以用同样的结构体保存不同的加密结构体。事实上,我跟进源代码,发现RSA的接口其实调用了EVP_PKEY的接口。因此,各种不同的非对称加密算法,就可以用同一个EVP的底层接口来实现,上层再次封装。这样的做法,可以节省开发时间。

RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **rsa, pem_password_cb *cb,void *u)
{
EVP_PKEY *pktmp;
pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
return pkey_get_rsa(pktmp, rsa);
}

        同样,openssl有自己的ssl,它定义了自己的一套文件操作方式,那就是BIO,BIO既可以封装用于网络通信的socket,也可以用于文件读取,而且还能够用于加密以及非加密的socket连接。正是如此,所以它既支持FILE存取,也支持BIO存取。看源码就可以看出,其实file的操作就是对bio进行一次封装,如下可以看出。

EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x, pem_password_cb *cb, void *u)
{
BIO *b;
EVP_PKEY *ret;
if ((b=BIO_new(BIO_s_file())) == NULL)
{
PEMerr(PEM_F_PEM_READ_PRIVATEKEY,ERR_R_BUF_LIB);
return(0);
}
BIO_set_fp(b,fp,BIO_NOCLOSE);
ret=PEM_read_bio_PrivateKey(b,x,cb,u);
BIO_free(b);
return(ret);
}


        下面分析EVP_PKEY。其实openssl将关于PEM的操作有一个统一的操作,将这些结构都保存在EVP_PKEY中。因为其实非对称加密方式有好几种:RSA、DSA、ECC,他们的原理不同,因此其密钥结构不同。下面是密钥生成函数,可以看出,密钥存在各自的不同的结构体中。

int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
int DSA_generate_key(DSA *dsa);
int EC_KEY_generate_key(EC_KEY *eckey);
int DH_generate_key(DH *dh);

RSA 结构rsa.h中,其中{n,e} 表示公钥,{n, d}表示私钥

struct rsa_st
{
.....
BIGNUM *n;
BIGNUM *e;
BIGNUM *d;
BIGNUM *p;
BIGNUM *q;
BIGNUM *dmp1;
BIGNUM *dmq1;
BIGNUM *iqmp;
        .....
};

DSA结构在dsa.h中

struct dsa_st
{
......
BIGNUM *p;
BIGNUM *q;/* == 20 */
BIGNUM *g;
BIGNUM *pub_key;  /* y public key */
BIGNUM *priv_key; /* x private key */
        ......
};


ECC结构在ecc.h

struct ec_key_st {
EC_GROUP *group;
EC_POINT *pub_key;
BIGNUM *priv_key;
} /* EC_KEY */;


DH结构体在dh.h中定义

struct dh_st
{

BIGNUM *p;
BIGNUM *g;
long length; /* optional */
BIGNUM *pub_key;/* g^x */
BIGNUM *priv_key;/* x */
}


接着分析EVP_KEP结构体,可以看出最有特点的是一个union结构。

struct evp_pkey_st
{
int type;
int save_type;
int references;
const EVP_PKEY_ASN1_METHOD *ameth;
ENGINE *engine;
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
#ifndef OPENSSL_NO_EC
struct ec_key_st *ec;/* ECC */
#endif
} pkey;
int save_parameters;
STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */
} /* EV

在union结构中分别定义rsa、dsa、dh、ec,这些不难理解,定义共用体的目的是节省内存,因为每次这个结构体只为一个非对称密码算法服务。而为什么定义一个ptr呢?这边封装的技巧就在这,ptr指向密钥结构的地址。不管生产的密钥是哪一种类型结构,将其强制转化为char* 赋值给ptr。而当我们调用时,我们根据type的类型,直接进行调用。比如说:EVP_KEY *pkey;pkey->pkey.rsa;直接就将ptr当成rsa类型操作。这就是共用体的好处。

参考资料:

http://fossies.org/dox/openssl-1.0.1f/index.html

原创粉丝点击