18

I have a server which would listen on HTTPS using OpenSSL. For this, I have to provide the certificate to use. However, the current implementation uses a filename to be provided to the OpenSSL API.

I want the certificate information to be read from memory, so that I don't have to ship the certificate file opening. I tried to google, but I didn't come up with any options.

Is is possible? If so, how do I read certificate files from memory instead of a file using OpenSSL?


EDIT: The following was moved from the comments to the question.

// 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
}
jww
  • 97,681
  • 90
  • 411
  • 885
Karthik
  • 361
  • 1
  • 2
  • 6
  • "I want the cert information to be read from memory, so that I don't have to ship the certificate file opening" - Can you clarify this? Not sure what you mean here. Where would the code be getting the cert in the first place? – Kamil Kisiel Sep 28 '10 at 06:01
  • 1
    I have the certificate files with me now. The server has to use them. Normal practice is to provide OpenSSL with the filename of certificate file. OpenSSL would take care of the remaining things internally. But I cannot ship the certificate files directly. I would HAVE to hard code them into the source code. That is the requirement. So, was looking at options where I have the certificate in a memory buffer and somehow make OpenSSL use that certificate information. – Karthik Sep 28 '10 at 06:25
  • Can you please specify what you are doing with some sample code? Certificate SSL contexts can be created from memory but it would be helpful if you can just share what you want to achieve with some code. – Praveen S Sep 28 '10 at 07:00

4 Answers4

18

The following code did the job for me:

 
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);
buc
  • 6,268
  • 1
  • 34
  • 51
Karthik
  • 361
  • 1
  • 2
  • 6
11

The other snippets will only load one certificate. The content of files like http://curl.haxx.se/ca/cacert.pem that contain a lot of different certificates need a new approach. This is adapted from openssl 1.0.1p (mostly openssl-1.0.1p\crypto\x509\by_file.c, char* buf contains the content of a *.pem file, ctx is a boost::asio::ssl::context), add error handling on your own:

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
  • 2,695
  • 34
  • 29
  • Thank you so much @oliver-zendel it perfectly did the job :) However, I got an error later, so I moved the line "BIO_free(cbio);" at the end. I used your code in a CURLOPT_SSL_CTX_FUNCTION callback in curl to read a PEM file with 2 certificates. – Kervala Mar 01 '16 at 12:02
  • Thanks Kervala, I changed the code snippet to be sure ;) – Oliver Zendel Apr 18 '16 at 14:03
  • I don't know if it was assumed, or if I have missed something, but unless I add a "main" certificate first (as described at the [earlier](https://stackoverflow.com/a/3812457/2369597) answer) before calling this code, no certificate is presented by the calling code hence the SSL handshake fails. – Wad Sep 03 '18 at 15:32
  • 1
    notice that per https://www.openssl.org/docs/man1.1.1/man3/X509_STORE_add_cert.html only trusted certificates should be added this way. not chains of certificates. loading complete chains should probably be loaded using SSL_CTX_use_certificate and SSL_CTX_add_extra_chain_cert – Itay Bianco Feb 14 '19 at 07:12
  • Itay is correct. The reason this does not work is that it is populating the trusted certificate store, not the server certificate chain. So, although this answer has a lot of votes, it is wrong. I posted a different answer below that should do the job. – Richard J. Smith Feb 08 '20 at 03:10
6
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);

Don't forget & before cert_data and pkey_data - and note that OpenSSL modifies these pointers.

caf
  • 233,326
  • 40
  • 323
  • 462
blaze
  • 4,326
  • 18
  • 23
  • 1
    if i understand correctly, the downside of this method is that it works for single certificates in DER format, as opposed to PEM which can include multiple certificate chains. – Itay Bianco Feb 13 '19 at 06:22
4

There is another response that uses X509_STORE_add_cert, which is up-voted but incorrect. That answer is a way to do SSL_CTX_load_verify_locations in memory, but does not load the server certificate chain. Replies to that comment also indicate that it does not work.

The following code is a load-from-memory implementation of SSL_CTX_use_certificate_chain_file based on the implementation of that function in 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;
}