c++ - 加载 PEM 格式证书

标签 c++ ubuntu ssl openssl

如何在 openssl c++ 中将 PEM 格式证书加载为 x509?

 int SSL_use_certificate(SSL *ssl, X509 *x);
 int SSL_use_certificate_ASN1(SSL *ssl, unsigned char *d, int len);
 int SSL_use_certificate_file(SSL *ssl, const char *file, int type);

这些是可用于将证书添加到句柄的 3 个函数。我在程序中有一个证书字符串(这只是一个 PEM 格式的数据)。我想把它加到句柄上。我该如何继续?

SSL_CTX_set_default_passwd_cb 是否可以使用我正在加载和 ssl 处理的私钥而不是上下文?

最佳答案

How do I load the PEM format certificate as an x509 in openssl c++?

您可能应该使用 SSL_CTX_use_certificate_chain_fileSSL_CTX_use_PrivateKey_file。您可以使用它们并构建客户端或服务器上下文。示例代码如下所示。使用它们有一些细微差别,因此请查看 OpenSSL 的文档,网址为 SSL_CTX_use_certificate(3)。 .

我不确定“作为 x509”是什么意思。证书将为 x509,但私钥将为 PKCS #8。有 PEM_read_bio_X509PEM_read_X509,它们返回一个 X509*,它们可能会执行您想要的操作。

Will SSL_CTX_set_default_passwd_cb work with private keys that I am loading

这取决于,但它应该。密码回调是可选的。如果您用密码保护 key ,请使用它。在下面的代码中,我将其称为PasswordCallback,它用于读取和写入 key (下面仅显示读取)。


using SSL_CTX_ptr = std::unique_ptr<SSL_CTX, decltype(&::SSL_CTX_free)>;

SSL_CTX* CreateServerContext()
{    
    do
    {
        int rc;
        unsigned long err;

        const SSL_METHOD* method = SSLv23_server_method();
        ASSERT(method != NULL);
        if (method == NULL)
        {
            LogError("GetServerContext: SSLv23_server_method failed");
            break; /* failed */
        }

        SSL_CTX_ptr t(SSL_CTX_new(method), ::SSL_CTX_free);
        ASSERT(t.get() != NULL);
        if (t.get() == NULL)
        {
            LogError("GetServerContext: SSL_CTX_new failed");
            break; /* failed */
        }

        long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
        flags |= SSL_OP_NO_COMPRESSION;
        flags |= SSL_OP_SAFARI_ECDHE_ECDSA_BUG;
        flags |= SSL_OP_CIPHER_SERVER_PREFERENCE;

        /* Cannot fail */
        SSL_CTX_set_options(t.get(), flags);

        string ciphers = GetServerCipherSuites();
        ASSERT(!ciphers.empty());

        rc = SSL_CTX_set_cipher_list(t.get(), ciphers.c_str());
        err = ERR_get_error();

        ASSERT(rc == 1);
        if (rc != 1)
        {
            LogError("GetServerContext: SSL_CTX_set_cipher_list failed");
            break; /* failed */
        }

        string certFile = config.GetServerCertFile();
        ASSERT(!certFile.empty());

        rc = SSL_CTX_use_certificate_chain_file(t.get(), certFile.c_str());
        err = ERR_get_error();

        ASSERT(rc == 1);
        if (rc != 1)
        {
            LogError("GetServerContext: SSL_CTX_use_certificate_chain_file failed");
            break; /* failed */
        }

        /* These two do not return a value... cannot fail? */
        SSL_CTX_set_default_passwd_cb(t.get(), PasswordCallback);
        SSL_CTX_set_default_passwd_cb_userdata(t.get(), (void*) SERVER_KEY_LABEL);

        string keyFile = config.GetServerKeyFile();
        ASSERT(!keyFile.empty());

        rc = SSL_CTX_use_PrivateKey_file(t.get(), keyFile.c_str(), SSL_FILETYPE_PEM);
        err = ERR_get_error();

        ASSERT(rc == 1);
        if (rc != 1)
        {
            LogError("GetServerContext: SSL_CTX_use_PrivateKey_file failed");
            break; /* failed */
        }

        rc = SSL_CTX_check_private_key(t.get());
        err = ERR_get_error();

        ASSERT(rc == 1);
        if (rc != 1)
        {
            LogError("GetServerContext: SSL_CTX_check_private_key failed");
            /* non-fatal, but everything will probably break */
        }

        /* These three do not return a value... cannot fail? */
        SSL_CTX_set_tmp_dh_callback(t.get(), DhCallback);
        SSL_CTX_set_tmp_ecdh_callback(t.get(), EcdhCallback);
        SSL_CTX_set_tlsext_servername_callback(t.get(), ServerNameCallback);

        return t.release();

    } while (0);

    return NULL;
}

密码回调的想法是:OpenSSL 为您提供缓冲区和大小。您填充缓冲区,并返回填充量的大小。

我的密码回调有点复杂。在将原始密码传递给库之前,它会对原始密码执行一次散列。这确保不使用“纯文本”密码(但不会减慢习惯性攻击)。您可以提示用户输入字符串,也可以返回硬编码字符串。

我的密码回调使用了一个标签。该标签允许我根据使用情况导出不同的 key (即使使用相同的“基本” secret )。通过指定不同的用法或标签,我得到了不同的 key 位推导。标签通过下面的 arg 提供,您可以使用 SSL_CTX_set_default_passwd_cb_userdata 进行设置。

using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_destroy)>;

int PasswordCallback(char *buffer, int size, int rwflag, void *arg)
{
    UNUSED(rwflag);

    int rc;
    unsigned long err;
    ostringstream oss;

    const char* label = (char*) arg;
    size_t lsize = (label ? strlen(label) : 0);

    SecureVector sv = config.GetMasterKey();
    AC_ASSERT(!sv.empty());
    if (sv.empty())
    {
        ...
        throw runtime_error(oss.str().c_str());
    }

    EVP_MD_CTX_ptr ctx(EVP_MD_CTX_create(), ::EVP_MD_CTX_destroy);
    AC_ASSERT(ctx.get() != NULL);

    const EVP_MD* hash = EVP_sha512();
    AC_ASSERT(hash != NULL);

    rc = EVP_DigestInit_ex(ctx.get(), hash, NULL);
    err = ERR_get_error();

    AC_ASSERT(rc == 1);
    if (rc != 1)
    {
        ...
        throw runtime_error(oss.str().c_str());
    }

    rc = EVP_DigestUpdate(ctx.get(), sv.data(), sv.size());
    err = ERR_get_error();

    AC_ASSERT(rc == 1);
    if (rc != 1)
    {
        ...
        throw runtime_error(oss.str().c_str());
    }

    if (label && lsize)
    {
        rc = EVP_DigestUpdate(ctx.get(), label, lsize);
        err = ERR_get_error();

        AC_ASSERT(rc == 1);
        if (rc != 1)
        {
            ...
            throw runtime_error(oss.str().c_str());
        }
    }

    int n = std::min(size, EVP_MD_size(hash));
    if (n <= 0)
        return 0;

    rc = EVP_DigestFinal_ex(ctx.get(), (unsigned char*) buffer, (unsigned int*) &n);
    err = ERR_get_error();

    AC_ASSERT(rc == 1);
    if (rc != 1)
    {
        ...
        throw runtime_error(oss.str().c_str());
    }

    return n;
}

关于c++ - 加载 PEM 格式证书,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24081470/

相关文章:

docker - server.properties 由于 ssl 配置而改变后重启 wurstmeister/kafka-docker

ssl - Zend Framework Not 路由错误

c++ - 如何检查两个不同类型的 vector 是否相等

python - 使用 pybind11 通过预先存在的嵌入式 python 解释器公开 C++ 功能

linux - 安装新主题时遇到问题 (Ubuntu 14.04)

mysql - 自动化 AppArmor 修改

c++ - 将对象转换为 vector 的 const_iterator

c++ - 在可变参数模板方法和普通模板方法之间进行选择的规则是什么?

bash - 从shell脚本中的行首删除字符?

php - https 非 www 重定向