【问题标题】:Read certificate files from memory instead of a file using OpenSSL从内存而不是使用 OpenSSL 的文件中读取证书文件
【发布时间】:2011-04-18 03:01:44
【问题描述】:

我有一个使用 OpenSSL 监听 HTTPS 的服务器。为此,我必须提供要使用的证书。但是,当前实现使用要提供给 OpenSSL API 的文件名。

我希望从内存中读取证书信息,这样我就不必发送证书文件打开。我尝试谷歌,但我没有想出任何选项。

有可能吗?如果是这样,我如何从内存而不是使用 OpenSSL 的文件中读取证书文件?


编辑:以下内容已从 cmets 移至问题。

// CURRENT
void start_server()
{
    const char *fileName = "cert_and_key.pem";
    set_server_ssl_file(fileName);
}
set_server_ssl_file(const char *fileName)
{
    //initialize context
    SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM); 
    SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM);
}

//REQUIRED
void start_server()
{
    const char *cert = "--BEGIN CERTIFICATE--............";
    const char *key = "--BEGIN RSA PRIVATE KEY--.......";
    set_server_ssl_options(cert, key);
}
set_server_ssl_options(const char *cert, const char *key)
{
    //IMPLEMENTATION REQUIRED
}

【问题讨论】:

  • “我希望从内存中读取证书信息,这样我就不必运送证书文件打开” - 你能澄清一下吗?不知道你在这里的意思。代码首先从哪里获得证书?
  • 我现在有证书文件。服务器必须使用它们。通常的做法是为 OpenSSL 提供证书文件的文件名。 OpenSSL 将在内部处理剩余的事情。但是我不能直接发送证书文件。我必须将它们硬编码到源代码中。这就是要求。因此,正在寻找我在内存缓冲区中拥有证书并以某种方式使 OpenSSL 使用该证书信息的选项。
  • 您能否具体说明您正在使用一些示例代码做什么?证书 SSL 上下文可以从内存中创建,但如果您可以通过一些代码分享您想要实现的目标,那将会很有帮助。

标签: c openssl certificate


【解决方案1】:

还有另一个使用X509_STORE_add_cert 的回复,该回复被赞成但不正确。该答案是一种在内存中执行SSL_CTX_load_verify_locations 的方法,但不会加载服务器证书链。对该评论的回复也表明它不起作用。

以下代码是SSL_CTX_use_certificate_chain_file 的从内存加载实现,基于该函数在 OpenSSL 中的实现:

bool load_cert_chain_from_shared_mem(SSL_CTX *context, const char *cert_buffer)
{
    BIO *cbio = BIO_new_mem_buf((void*)cert_buffer, -1);
    if (!cbio)
        return false;

    X509_INFO *itmp;
    int i, count = 0, type = X509_FILETYPE_PEM;
    STACK_OF(X509_INFO) *inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL);

    if (!inf)
    {
        BIO_free(cbio);
        return false;
    }

    /* Iterate over contents of the PEM buffer, and add certs. */
    BOOL first = TRUE;
    for (i = 0; i < sk_X509_INFO_num(inf); i++) {
        itmp = sk_X509_INFO_value(inf, i);
        if (itmp->x509)
        {
            /* First cert is server cert. Remaining, if any, are intermediate certs. */
            if (first)
            {
                first = FALSE;

                /*
                 * Set server certificate. Note that this operation increments the
                 * reference count, which means that it is okay for cleanup to free it.
                 */
                if (!SSL_CTX_use_certificate(context, itmp->x509))
                    goto Error;

                if (ERR_peek_error() != 0)
                    goto Error;

                /* Get ready to store intermediate certs, if any. */
                SSL_CTX_clear_chain_certs(context);
            }
            else
            {
                /* Add intermediate cert to chain. */
                if (!SSL_CTX_add0_chain_cert(context, itmp->x509))
                    goto Error;

                /*
                 * Above function doesn't increment cert reference count. NULL the info
                 * reference to it in order to prevent it from being freed during cleanup.
                 */
                itmp->x509 = NULL;
            }
        }
    }

    sk_X509_INFO_pop_free(inf, X509_INFO_free);
    BIO_free(cbio);

    return true;

Error:
    sk_X509_INFO_pop_free(inf, X509_INFO_free);
    BIO_free(cbio);

    return false;
}

【讨论】:

  • 不错!正是我想要的!谢谢!
【解决方案2】:

其他 sn-ps 只会加载一个证书。像http://curl.haxx.se/ca/cacert.pem 这样包含许多不同证书的文件的内容需要一种新的方法。这是从 openssl 1.0.1p 改编而来的(主要是 openssl-1.0.1p\crypto\x509\by_file.c,char* buf 包含 *.pem 文件的内容,ctx 是 boost::asio::ssl::context ),自行添加错误处理:

BIO *cbio = BIO_new_mem_buf((void*)buf, (int)length);
X509_STORE  *cts = SSL_CTX_get_cert_store(ctx.native_handle());
if(!cts || !cbio)
   return false;
X509_INFO *itmp;
int i, count = 0, type = X509_FILETYPE_PEM;
STACK_OF(X509_INFO) *inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL);

if (!inf)
{
    BIO_free(cbio);//cleanup
    return false;
}
//itterate over all entries from the pem file, add them to the x509_store one by one
for (i = 0; i < sk_X509_INFO_num(inf); i++) {
    itmp = sk_X509_INFO_value(inf, i);
    if (itmp->x509) {
          X509_STORE_add_cert(cts, itmp->x509);
          count++;
    }
    if (itmp->crl) {
          X509_STORE_add_crl(cts, itmp->crl);
          count++;
    }
}
sk_X509_INFO_pop_free(inf, X509_INFO_free); //cleanup
BIO_free(cbio);//cleanup

【讨论】:

  • 非常感谢@oliver-zendel,它完美地完成了这项工作:) 但是,我后来遇到了一个错误,所以我移动了“BIO_free(cbio);”这一行在末尾。我在 curl 的 CURLOPT_SSL_CTX_FUNCTION 回调中使用了您的代码来读取具有 2 个证书的 PEM 文件。
  • 感谢 Kervala,我更改了代码 sn-p 以确保 ;)
  • 我不知道它是否是假设的,或者我是否遗漏了什么,但除非我在调用此代码之前先添加“主”证书(如earlier 回答中所述),调用代码没有提供证书,因此 SSL 握手失败。
  • 请注意,每个openssl.org/docs/man1.1.1/man3/X509_STORE_add_cert.html 只能以这种方式添加受信任的证书。不是证书链。加载完整的链可能应该使用 SSL_CTX_use_certificate 和 SSL_CTX_add_extra_chain_cert 加载
  • Itay 是正确的。这不起作用的原因是它正在填充受信任的证书存储,而不是服务器证书链。所以,虽然这个答案有很多票,但它是错误的。我在下面发布了一个不同的答案,应该可以完成这项工作。
【解决方案3】:

以下代码为我完成了这项工作:

 
SSL_CTX *CTX;
X509 *cert = NULL;
RSA *rsa = NULL;
BIO *cbio, *kbio;
const char *cert_buffer = "";
const char *key_buffer = "";

cbio = BIO_new_mem_buf((void*)cert_buffer, -1);
cert = PEM_read_bio_X509(cbio, NULL, 0, NULL);
assert(cert != NULL);
SSL_CTX_use_certificate(CTX, cert);

kbio = BIO_new_mem_buf((void*)key_buffer, -1);
rsa = PEM_read_bio_RSAPrivateKey(kbio, NULL, 0, NULL);
assert(rsa != NULL);
SSL_CTX_use_RSAPrivateKey(CTX, rsa);

【讨论】:

  • 你不应该忘记相应地释放分配的缓冲区。
【解决方案4】:
unsigned char *cert_data = (....);
int cert_len = (....);

X509 *cert = d2i_X509(NULL, &cert_data, cert_len);
SSL_CTX_use_certificate(ctx, cert);

unsigned char *pkey_data = /* ... */;
int pkey_len = /* ... */;

RSA *pkey = d2i_RSAPrivateKey(NULL, &pkey_data, pkey_len);
SSL_CTX_use_RSAPrivateKey(ctx, pkey);

不要忘记在cert_datapkey_data 之前的&amp; - 请注意OpenSSL 会修改这些指针。

【讨论】:

  • 如果我理解正确的话,这种方法的缺点是它适用于 DER 格式的单个证书,而不是可以包含多个证书链的 PEM。
猜你喜欢
  • 1970-01-01
  • 2010-10-08
  • 2019-03-21
  • 1970-01-01
  • 2012-09-01
  • 2013-07-06
  • 1970-01-01
  • 2019-03-26
  • 1970-01-01
相关资源
最近更新 更多