OpenSSL之SSL_CTX_use_PrivateKey_file分析

  • Post author:
  • Post category:其他




OpenSSL之SSL_CTX_use_PrivateKey_file分析

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


前言

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



一、

SSL_CTX_use_PrivateKey_file

是什么?

SSL_CTX_use_PrivateKey_file是openssl中的加载密钥的函数,函数申明为:

int SSL_CTX_use_PrivateKey_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_PrivateKey_file(SSL_CTX *ctx, const char *file, int type)
{
    int j, ret = 0;
    BIO *in;
    EVP_PKEY *pkey = NULL;

    in = BIO_new(BIO_s_file_internal());
    if (in == NULL) {
        SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_BUF_LIB);
        goto end;
    }

    if (BIO_read_filename(in, file) <= 0) {
        SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_SYS_LIB);
        goto end;
    }
    if (type == SSL_FILETYPE_PEM) {
        j = ERR_R_PEM_LIB;
        pkey = PEM_read_bio_PrivateKey(in, NULL,
                                       ctx->default_passwd_callback,
                                       ctx->default_passwd_callback_userdata);
    } else if (type == SSL_FILETYPE_ASN1) {
        j = ERR_R_ASN1_LIB;
        pkey = d2i_PrivateKey_bio(in, NULL);
    } else {
        SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, SSL_R_BAD_SSL_FILETYPE);
        goto end;
    }
    if (pkey == NULL) {
        SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, j);
        goto end;
    }
    ret = SSL_CTX_use_PrivateKey(ctx, pkey);
    EVP_PKEY_free(pkey);
 end:
    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_PrivateKey_bio(in, NULL)将密钥文件加载到EVP_PKEY句柄中,当type == SSL_FILETYPE_PEM时调用PEM_read_bio_PrivateKey(in, NULL,ctx->default_passwd_callback, ctx->default_passwd_callback_userdata)将密钥文件加载到EVP_PKEY句柄中,文件加载失败则goto end;释放句柄,退出操作。

加载密钥文件成功后调用ret = SSL_CTX_use_PrivateKey(ctx, pkey)将密钥设置到ctx上下文中,下面我们来看看SSL_CTX_use_PrivateKey函数的实现。

int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey)
{
    if (pkey == NULL) {
        SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_PASSED_NULL_PARAMETER);
        return (0);
    }
    if (!ssl_cert_inst(&ctx->cert)) {
        SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_MALLOC_FAILURE);
        return (0);
    }
    return (ssl_set_pkey(ctx->cert, pkey));
}

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

static int ssl_set_pkey(CERT *c, EVP_PKEY *pkey)
{
    int i;
    /*
     * Special case for DH: check two DH certificate types for a match. This
     * means for DH certificates we must set the certificate first.
     */
    if (pkey->type == EVP_PKEY_DH) {
        X509 *x;
        i = -1;
        x = c->pkeys[SSL_PKEY_DH_RSA].x509;
        if (x && X509_check_private_key(x, pkey))
            i = SSL_PKEY_DH_RSA;
        x = c->pkeys[SSL_PKEY_DH_DSA].x509;
        if (i == -1 && x && X509_check_private_key(x, pkey))
            i = SSL_PKEY_DH_DSA;
        ERR_clear_error();
    } else
        i = ssl_cert_type(NULL, pkey);
    if (i < 0) {
        SSLerr(SSL_F_SSL_SET_PKEY, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
        return (0);
    }

    if (c->pkeys[i].x509 != NULL) {
        EVP_PKEY *pktmp;
        pktmp = X509_get_pubkey(c->pkeys[i].x509);
        if (pktmp == NULL) {
            SSLerr(SSL_F_SSL_SET_PKEY, ERR_R_MALLOC_FAILURE);
            EVP_PKEY_free(pktmp);
            return 0;
        }
        /*
         * The return code from EVP_PKEY_copy_parameters is deliberately
         * ignored. Some EVP_PKEY types cannot do this.
         */
        EVP_PKEY_copy_parameters(pktmp, pkey);
        EVP_PKEY_free(pktmp);
        ERR_clear_error();

#ifndef OPENSSL_NO_RSA
        /*
         * Don't check the public/private key, this is mostly for smart
         * cards.
         */
        if ((pkey->type == EVP_PKEY_RSA) &&
            (RSA_flags(pkey->pkey.rsa) & RSA_METHOD_FLAG_NO_CHECK)) ;
        else
#endif
        if (!X509_check_private_key(c->pkeys[i].x509, pkey)) {
            X509_free(c->pkeys[i].x509);
            c->pkeys[i].x509 = NULL;
            return 0;
        }
    }

    if (c->pkeys[i].privatekey != NULL)
        EVP_PKEY_free(c->pkeys[i].privatekey);
    CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
    c->pkeys[i].privatekey = pkey;
    c->key = &(c->pkeys[i]);

    c->valid = 0;
    return (1);
}

首先检查密钥类型是否是EVP_PKEY_DH,如果是则调用X509_check_private_key来判断属于哪一类DH密钥,SSL_PKEY_DH_RSA还是SSL_PKEY_DH_DSA,然后记录对应的下标。如果不是EVP_PKEY_DH类型的密钥则调用ssl_cert_type(NULL, 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对应的下标上,最后释放句柄返回成功。




总结

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



版权声明:本文为u012023606原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。