本系列OpenSSL使用的代码版本为:1.0.2o
本篇文章纯属个人学习的一点经验分享,若有不对之处烦请各位大神现身指点,希望能和大家一起共同进步。
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:成功
代码开头调用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的同学起到帮助。
