OpenSSL之SSL

tech2026-02-21  9

OpenSSL之SSL_CTX_use_certificate_file分析

本系列OpenSSL使用的代码版本为:1.0.2o


前言

本篇文章纯属个人学习的一点经验分享,若有不对之处烦请各位大神现身指点,希望能和大家一起共同进步。


 

一、SSL_use_certificate_file是什么?

SSL_CTX_use_certificate_file是openssl代码中的证书调用函数,函数申明为:

int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);

参数:

ctx:SSL的上下文句柄

file:证书文件的路径

type:证书文件的类型,

         SSL_FILETYPE_ASN1:ASN.1格式又称DER格式。

         SSL_FILETYPE_PEM:PEM格式,简单点讲就是DER格式经过base64计算后的格式。

返回值:

0:失败

1:成功

二、代码分析

int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) { int j; BIO *in; int ret = 0; X509 *x = NULL; in = BIO_new(BIO_s_file_internal()); if (in == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_BUF_LIB); goto end; } if (BIO_read_filename(in, file) <= 0) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_SYS_LIB); goto end; } if (type == SSL_FILETYPE_ASN1) { j = ERR_R_ASN1_LIB; x = d2i_X509_bio(in, NULL); } else if (type == SSL_FILETYPE_PEM) { j = ERR_R_PEM_LIB; x = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, ctx->default_passwd_callback_userdata); } else { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, SSL_R_BAD_SSL_FILETYPE); goto end; } if (x == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, j); goto end; } ret = SSL_CTX_use_certificate(ctx, x); end: if (x != NULL) X509_free(x); if (in != NULL) BIO_free(in); return (ret); }

        代码开头调用BIO_new(BIO_s_file_internal())为读取证书申请一个文件类型的BIO,然后调用BIO_read_filename(in, file)将证书文件设置到BIO中,然后根据证书类型不同调用不用的证书加载方法,type == SSL_FILETYPE_ASN1时调用d2i_X509_bio(in, NULL)将证书文件加载到X509句柄中,当type == SSL_FILETYPE_PEM时调用PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback,ctx->default_passwd_callback_userdata)将证书文件加载到X509句柄中,证书加载失败则goto end;释放句柄,退出操作。

        加载证书成功后调用ret = SSL_CTX_use_certificate(ctx, x);将证书设置到ctx上下文中,下面我们来看看SSL_CTX_use_certificate函数的实现。

int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) { if (x == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE, ERR_R_PASSED_NULL_PARAMETER); return (0); } if (!ssl_cert_inst(&ctx->cert)) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE, ERR_R_MALLOC_FAILURE); return (0); } return (ssl_set_cert(ctx->cert, x)); }

        开头是常见的判NULL操作,不管,接着是ssl_cert_inst(&ctx->cert)操作,用来查看ctx句柄中cert字段是否存在内容,存在则什么都不干,不存在则调用ssl_cert_new()函数申请一个CERT赋值给ctx->cert,接着调用ssl_set_cert(ctx->cert, x)将证书x设置到ctx->cert中,下面我们来看看ssl_set_cert函数的实现。

static int ssl_set_cert(CERT *c, X509 *x) { EVP_PKEY *pkey; int i; pkey = X509_get_pubkey(x); if (pkey == NULL) { SSLerr(SSL_F_SSL_SET_CERT, SSL_R_X509_LIB); return (0); } i = ssl_cert_type(x, pkey); if (i < 0) { SSLerr(SSL_F_SSL_SET_CERT, SSL_R_UNKNOWN_CERTIFICATE_TYPE); EVP_PKEY_free(pkey); return (0); } if (c->pkeys[i].privatekey != NULL) { /* * The return code from EVP_PKEY_copy_parameters is deliberately * ignored. Some EVP_PKEY types cannot do this. */ EVP_PKEY_copy_parameters(pkey, c->pkeys[i].privatekey); ERR_clear_error(); #ifndef OPENSSL_NO_RSA /* * Don't check the public/private key, this is mostly for smart * cards. */ if ((c->pkeys[i].privatekey->type == EVP_PKEY_RSA) && (RSA_flags(c->pkeys[i].privatekey->pkey.rsa) & RSA_METHOD_FLAG_NO_CHECK)) ; else #endif /* OPENSSL_NO_RSA */ if (!X509_check_private_key(x, c->pkeys[i].privatekey)) { /* * don't fail for a cert/key mismatch, just free current private * key (when switching to a different cert & key, first this * function should be used, then ssl_set_pkey */ EVP_PKEY_free(c->pkeys[i].privatekey); c->pkeys[i].privatekey = NULL; /* clear error queue */ ERR_clear_error(); } } EVP_PKEY_free(pkey); if (c->pkeys[i].x509 != NULL) X509_free(c->pkeys[i].x509); CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); c->pkeys[i].x509 = x; c->key = &(c->pkeys[i]); c->valid = 0; return (1); }

        首先调用pkey = X509_get_pubkey(x)获取公钥信息,然后调用i = ssl_cert_type(x, pkey)获取证书类型对应的数组下标,如:

# define SSL_PKEY_RSA_ENC        0 # define SSL_PKEY_RSA_SIGN       1 # define SSL_PKEY_DSA_SIGN       2 # define SSL_PKEY_DH_RSA         3 # define SSL_PKEY_DH_DSA         4 # define SSL_PKEY_ECC            5 # define SSL_PKEY_GOST94         6 # define SSL_PKEY_GOST01         7 # define SSL_PKEY_NUM            8

然后根据下标判断ctx->cert对应位置的私钥是否存在,如果存在则判断公私钥是否匹配,如果使用了不匹配的密钥和证书就会在这个时候检测出错,然后退出函数,检测通过之后就将x509证书指针设置到ctx->cert对应的下标中进行存储,最后释放不需要的句柄返回成功。


总结

好了,讲到这里差不多将SSL_use_certificate_file流程过了一遍,希望对其他有兴趣学习OpenSSL的同学起到帮助。

最新回复(0)