本系列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:成功
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的同学起到帮助。