Get entire X509 certificate chain using OpenSSL - c

I'm trying to get the entire certificate chain for a given domain using the following code (error checking removed for brevity):
int myFunction() {
for (int i = 0; i < CERTIFICATE_CHAIN_MAXIMUM; i++) {
certificateChain[i] = NULL;
}
numberOfCerts = 0;
OPENSSL_init();
SSL_library_init();
OpenSSL_add_all_ciphers();
ERR_load_SSL_strings();
SSL_CTX * ctx = NULL;
BIO * web = NULL;
SSL * ssl = NULL;
ctx = SSL_CTX_new(TLSv1_2_client_method());
SSL_CTX_set_verify_depth(ctx, CERTIFICATE_CHAIN_MAXIMUM);
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_callback);
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
web = BIO_new_ssl_connect(ctx);
BIO_set_conn_hostname(web, host) // host defined elasewhere
BIO_get_ssl(web, &ssl);
const char* const PREFERRED_CIPHERS = "HIGH:!aNULL:!MD5:!RC4";
SSL_set_cipher_list(ssl, PREFERRED_CIPHERS)
SSL_set_tlsext_host_name(ssl, [URL.host UTF8String])
BIO_do_connect(web)
BIO_do_handshake(web)
}
int verify_callback(int preverify, X509_STORE_CTX* x509_ctx)
{
STACK_OF(X509) * certs = X509_STORE_CTX_get1_chain(x509_ctx);
X509 * cert;
for (int i = 0, count = sk_X509_num(certs); i < count; i++) {
if (i < CERTIFICATE_CHAIN_MAXIMUM) {
cert = sk_X509_value(certs, i);
if (cert != NULL) {
certificateChain[i] = cert;
numberOfCerts ++;
}
} else {
printf("Certificate chain maximum exceeded.");
}
}
return preverify;
}
The problem that I'm running into is that I'm only getting the client certificate, and intermediate CA(s), but not the root CA.
For example, the certificate chain for github.com is:
DigiCert High Assurance EV Root CA
> DigiCert SHA2 Extended Validation Server CA
> github.com
But with my code, I only end up with:
DigiCert SHA2 Extended Validation Server CA
> github.com
I'm assuming that the issue is that I have to recursively get the parent certificate for each certificate, however I'm unsure about how to do this and struggle to find anything helpful in terms of documentation.

Related

migrated key loading api's using openssl 3.0.7, failed to give same signing file

migrated key loading api's using openssl 3.0.7, failed to give same signing file.
we are migrating the our application code from openssl 1.1.1q(case 1) to 3.0.7(case 2).
we are trying to sign the same file using above openssl version but migrated code or case2 is we are suspecting a key loading is not proper.
we are fetching n, e , d values from our private key and loading to PKEY in both case 1 and 2.
Please let me know where we missed or any suggestion on key loading part.
case 1:KEY loading using openssl 1.1.1q:where privateModulus and privateExponent are from our private key
EVP_PKEY *pk8_key;
pk8_key = EVP_PKEY_new();
RSA *rsa = RSA_new();
BIGNUM *rsa_n, *rsa_e, *rsa_d;
rsa_n = BN_new();
rsa_n=BN_bin2bn((const unsigned char*)privateModulus,(int)expModLength,rsa_n);
if (!(rsa_n))
{
throw_exception("Error in assigning modulus(n)");
}
rsa_d = BN_new();
rsa_d=BN_bin2bn((const unsigned char*)privateExponent,(int)expModLength,rsa_d);
if (!(rsa_d))
{
throw_exception("Error in assigning exponent(d)");
}
rsa_e = BN_new();
if(!BN_hex2bn(&rsa_e,"0x03"))
{
throw_exception("Error in assigning exponent(d)");
}
// Note - memory management of BIGNUMS are transfered and freed with RSA object.
int ret = RSA_set0_key(rsa,rsa_n,rsa_e,rsa_d);
if (1 != ret)
{
throw_exception("Error creating RSA key from inputs");
}
RSA_blinding_off(rsa);
rsa_assign_status = EVP_PKEY_assign_RSA(pk8_key, rsa);
case2: KEY loading using openssl 3.0.7:where privateModulus and privateExponent are from our private key
EVP_PKEY* pk8_key = NULL;
EVP_PKEY_CTX* ctx = NULL;
BIGNUM* rsa_n =NULL , * rsa_d=NULL;
OSSL_PARAM* params = NULL;
OSSL_PARAM_BLD* bld = NULL;
OSSL_LIB_CTX *libctx = NULL;
int result = 0,i;
unsigned int rsa_e = 0;
rsa_n = BN_new();
rsa_n=BN_bin2bn((const unsigned char*)privateModulus,(int)expModLength,rsa_n);
if (!(rsa_n))
{
throw_exception("Error in assigning modulus(n)");
}
rsa_d = BN_new();
rsa_d=BN_bin2bn((const unsigned char*)privateExponent,(int)expModLength,rsa_d);
if (!(rsa_d))
{
throw_exception("Error in assigning exponent(d)");
}
rsa_e = 0x03;
bld = OSSL_PARAM_BLD_new();
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, rsa_n);
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, rsa_d);
//OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, rsa_e);
OSSL_PARAM_BLD_push_uint(bld, OSSL_PKEY_PARAM_RSA_E, rsa_e);
params = OSSL_PARAM_BLD_to_param(bld);
libctx = OSSL_LIB_CTX_new();
ctx = EVP_PKEY_CTX_new_from_name( libctx, "RSA", NULL );
if (ctx == NULL || EVP_PKEY_fromdata_init(ctx) <= 0
|| EVP_PKEY_fromdata(ctx, &pk8_key, EVP_PKEY_KEYPAIR, params) <= 0)
{
printf("error");
}
Not sure whether EVP_PKEY_fromdata will assign params same as EVP_PKEY_assign_RSA api.
we tried to set params different in many ways but not sure whether fromdata will assign params same as assign_rsa api.
we tried to sign the same file using both case and expecting same result but using our new way of constructing key we are unable to get same sign.
Please let us know about our key construction part and for same input file signing should be same or not.

Programmatically verify certificate chain using OpenSSL API

This is very similar to other questions but the ones I've looked at either don't have an answer or don't quite ask the same question. I have a self-signed CA certificate, and two other certificates that are signed with that CA certificate. I'm fairly sure the certificates are correct, because 'openssl verify' works:
$ openssl verify -CAfile ca.pem server.pem
server.pem: OK
(The above is from memory, I don't have them in front of me, so it may be slightly off).
Now I want to verify the certificates programatically. I have a utility function with pseudocode below:
int verify_cert(X509 *cert, X509 *cacert)
{
int ret;
X509_STORE *store;
X509_STORE_CTX *ctx;
store = X509_STORE_new();
X590_STORE_add_cert(store, cacert);
ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(ctx, store, cert, NULL);
ret = X509_verify_cert(ctx);
/* check for errors and clean up */
}
My problem is that the above code always returns 'failed to find issuer certificate'. What have I done wrong? I believe I am creating a new store, adding the cacert, creating a new context, and adding the child cert to be verified to the context with a pointer to the store which contains the CA. I'm pretty obviously doing something wrong, but I'm unsure what.
Any ideas?
Update: I'm aware I can save these certs to disk and use something like X509_LOOKUP_file or something similar. I'm looking for a solution that doesn't touch the disk unnecessarily.
You can use the normal validation routines (see How do you verify a public key was issued by your private CA?), like the -verify function in OpenSSL does. You need to create a lookup method (X509_LOOKUP_METHOD) like X509_LOOKUP_file(), but which works with a character string instead of a filename. The code for X509_LOOKUP_buffer() is as follows.
Header file by_buffer.h:
/* File: by_buffer.h */
#ifndef BY_BUFFER_H
#define BY_BUFFER_H
#include <openssl/x509.h>
#ifdef __cplusplus
extern "C" {
#endif
#define X509_L_BUF_LOAD 1
#define X509_LOOKUP_load_buf(x,name,type) \
X509_LOOKUP_ctrl((x),X509_L_BUF_LOAD,(name),(long)(type),NULL)
X509_LOOKUP_METHOD *X509_LOOKUP_buffer(void);
#ifdef __cplusplus
}
#endif
#endif /* BY_BUFFER_H */
The c program by_buffer.c:
/* by_buffer.c - copied and modified from crypto/x509/by_file.c */
/* Copyright (C) - should be the same as for OpenSSL
*/
#include "by_buffer.h"
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include "../crypto/cryptlib.h"
#include <openssl/lhash.h>
#include <openssl/buffer.h>
#include <openssl/pem.h>
#include <openssl/err.h>
static int by_buffer_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
long argl, char **ret);
X509_LOOKUP_METHOD x509_buffer_lookup=
{
"Load buffer into cache",
NULL, /* new */
NULL, /* free */
NULL, /* init */
NULL, /* shutdown */
by_buffer_ctrl, /* ctrl */
NULL, /* get_by_subject */
NULL, /* get_by_issuer_serial */
NULL, /* get_by_fingerprint */
NULL, /* get_by_alias */
};
X509_LOOKUP_METHOD *X509_LOOKUP_buffer(void)
{
return(&x509_buffer_lookup);
}
static int by_buffer_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
char **ret)
{
int ok=0;
char *certBuf;
switch (cmd)
{
case X509_L_BUF_LOAD:
if (argl == X509_FILETYPE_DEFAULT)
{
X509err(X509_F_BY_FILE_CTRL,X509_R_LOADING_DEFAULTS);
}
else
{
if(argl == X509_FILETYPE_PEM)
ok = (X509_load_cert_crl_buf(ctx,argp,
X509_FILETYPE_PEM) != 0);
else
ok = (X509_load_cert_buf(ctx,argp,(int)argl) != 0);
}
break;
}
return(ok);
}
int X509_load_cert_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
{
int ret=0;
BIO *in=NULL;
int i,count=0;
X509 *x=NULL;
if (certBuf == NULL) return(1);
in=BIO_new(BIO_s_mem());
if(in==NULL) goto err;
if (type == X509_FILETYPE_PEM)
{
for (;;)
{
x=PEM_read_bio_X509_AUX(in,NULL,NULL,NULL);
if (x == NULL)
{
if ((ERR_GET_REASON(ERR_peek_last_error()) ==
PEM_R_NO_START_LINE) && (count > 0))
{
ERR_clear_error();
break;
}
else
{
X509err(X509_F_X509_LOAD_CERT_FILE,
ERR_R_PEM_LIB);
goto err;
}
}
i=X509_STORE_add_cert(ctx->store_ctx,x);
if (!i) goto err;
count++;
X509_free(x);
x=NULL;
}
ret=count;
}
else if (type == X509_FILETYPE_ASN1)
{
x=d2i_X509_bio(in,NULL);
if (x == NULL)
{
X509err(X509_F_X509_LOAD_CERT_FILE,ERR_R_ASN1_LIB);
goto err;
}
i=X509_STORE_add_cert(ctx->store_ctx,x);
if (!i) goto err;
ret=i;
}
else
{
X509err(X509_F_X509_LOAD_CERT_FILE,X509_R_BAD_X509_FILETYPE);
goto err;
}
err:
if (x != NULL) X509_free(x);
if (in != NULL) BIO_free(in);
return(ret);
}
int X509_load_crl_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
{
int ret=0;
BIO *in=NULL;
int i,count=0;
X509_CRL *x=NULL;
if (certBuf == NULL) return(1);
//in=BIO_new(BIO_s_file_internal());
in=BIO_new(BIO_s_mem());
if(in==NULL) goto err;
if (type == X509_FILETYPE_PEM)
{
for (;;)
{
x=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
if (x == NULL)
{
if ((ERR_GET_REASON(ERR_peek_last_error()) ==
PEM_R_NO_START_LINE) && (count > 0))
{
ERR_clear_error();
break;
}
else
{
X509err(X509_F_X509_LOAD_CRL_FILE,
ERR_R_PEM_LIB);
goto err;
}
}
i=X509_STORE_add_crl(ctx->store_ctx,x);
if (!i) goto err;
count++;
X509_CRL_free(x);
x=NULL;
}
ret=count;
}
else if (type == X509_FILETYPE_ASN1)
{
x=d2i_X509_CRL_bio(in,NULL);
if (x == NULL)
{
X509err(X509_F_X509_LOAD_CRL_FILE,ERR_R_ASN1_LIB);
goto err;
}
i=X509_STORE_add_crl(ctx->store_ctx,x);
if (!i) goto err;
ret=i;
}
else
{
X509err(X509_F_X509_LOAD_CRL_FILE,X509_R_BAD_X509_FILETYPE);
goto err;
}
err:
if (x != NULL) X509_CRL_free(x);
if (in != NULL) BIO_free(in);
return(ret);
}
int X509_load_cert_crl_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
{
STACK_OF(X509_INFO) *inf;
X509_INFO *itmp;
BIO *in;
int i, count = 0;
if(type != X509_FILETYPE_PEM)
return X509_load_cert_buf(ctx, certBuf, type);
in = BIO_new(BIO_s_mem());
if(!in) {
X509err(X509_F_X509_LOAD_CERT_CRL_FILE,ERR_R_SYS_LIB);
return 0;
}
BIO_write(in, certBuf, strlen(certBuf));
inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
BIO_free(in);
if(!inf) {
X509err(X509_F_X509_LOAD_CERT_CRL_FILE,ERR_R_PEM_LIB);
return 0;
}
for(i = 0; i < sk_X509_INFO_num(inf); i++) {
itmp = sk_X509_INFO_value(inf, i);
if(itmp->x509) {
X509_STORE_add_cert(ctx->store_ctx, itmp->x509);
count++;
}
if(itmp->crl) {
X509_STORE_add_crl(ctx->store_ctx, itmp->crl);
count++;
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
return count;
}
Routine in C++ which calls the above routines:
#include "by_buffer.h"
static int check(X509_STORE *ctx, const char *certBuf);
static X509 *load_cert(const char *certBuf);
int validateKey(const char *rsaKeyCA, const char *rsaCertificate) {
int ret=0;
X509_STORE *cert_ctx=NULL;
X509_LOOKUP *lookup=NULL;
cert_ctx=X509_STORE_new();
if (cert_ctx == NULL) goto end;
OpenSSL_add_all_algorithms();
lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_buffer());
if (lookup == NULL)
goto end;
if(!X509_LOOKUP_load_buf(lookup,rsaKeyCA,X509_FILETYPE_PEM))
goto end;
lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_hash_dir());
if (lookup == NULL)
goto end;
X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
ret = check(cert_ctx, rsaCertificate);
end:
if (cert_ctx != NULL) X509_STORE_free(cert_ctx);
return ret;
}
static X509 *load_cert(const char *certBuf)
{
X509 *x=NULL;
BIO *cert;
if ((cert=BIO_new(BIO_s_mem())) == NULL)
goto end;
BIO_write(cert, certBuf, strlen(certBuf));
x=PEM_read_bio_X509_AUX(cert,NULL, NULL, NULL);
end:
if (cert != NULL) BIO_free(cert);
return(x);
}
static int check(X509_STORE *ctx, const char *certBuf)
{
X509 *x=NULL;
int i=0,ret=0;
X509_STORE_CTX *csc;
x = load_cert(certBuf);
if (x == NULL)
goto end;
csc = X509_STORE_CTX_new();
if (csc == NULL)
goto end;
X509_STORE_set_flags(ctx, 0);
if(!X509_STORE_CTX_init(csc,ctx,x,0))
goto end;
////// See crypto/asn1/t_x509.c for ideas on how to access and print the values
//printf("X.509 name: %s\n", x->name);
i=X509_verify_cert(csc);
X509_STORE_CTX_free(csc);
ret=0;
end:
ret = (i > 0);
if (x != NULL)
X509_free(x);
return(ret);
}
I think, you can use "X509_STORE_set_verify_cb" to add a callback to identify the actual error:
static int verify_cb(int ok, X509_STORE_CTX *ctx)
{
if (!ok)
{
/* check the error code and current cert*/
X509 *currentCert = X509_STORE_CTX_get_current_cert(ctx);
int certError = X509_STORE_CTX_get_error(ctx);
int depth = X509_STORE_CTX_get_error_depth(ctx);
printCert(currentCert);
printf("Error depth %d, certError %d", depth, certError)
}
return(ok);
}
int verify_cert(X509 *cert, X509 *cacert)
{
int ret;
X509_STORE *store;
X509_STORE_CTX *ctx;
store = X509_STORE_new();
X509_STORE_set_verify_cb(store, verify_cb);
X590_STORE_add_cert(store, cacert);
ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(ctx, store, cert, NULL);
ret = X590_verify_cert(ctx);
/* check for errors and clean up */
}
Unless we know the error code it is difficult to guess the actual problem. The code otherwise looks OK.
I encountered this problem myself and started off with code very close to the OP. My certificate chain included 3 certificates:
Certificate 1 (root-ca) Issuer: root-ca Subject: root-ca
Certificate 2 (signing-ca) Issuer: root-ca Subject: signing-ca
Certificate 3 (device) Issuer: signing-ca Subject: device
I wanted to verify the device certificate. My ca.pem equivalent (wrt OP) contained the root-ca and signing-ca.
The X509_verify_cert function needs the entire certificate chain all the way to the root (root-ca & signing-ca) in the X509_store.
Below is my code that works for me. Checks on return values were omitted to lean the code down.
int getIssuerCert(X509_STORE *x509_store){
STACK_OF(X509_INFO) *inf;
X509_INFO *itmp;
BIO *in;
int i, count = 0;
in = BIO_new(BIO_s_mem());
BIO_write(in, issuerCertStr, strlen(issuerCertStr)); //string containing root-ca & signing-ca
inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
if(in != NULL) BIO_free(in);
for(i = 0; i < sk_X509_INFO_num(inf); i++) {
itmp = sk_X509_INFO_value(inf, i);
if(itmp->x509) {
X509_STORE_add_cert(x509_store, itmp->x509);
count++;
}
if(itmp->crl) {
X509_STORE_add_crl(x509_store, itmp->crl);
count++;
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
return 0;
}
int verify_cert(){
int ret = 0;
X509 *devCert = NULL;
X509_STORE *x509_store = NULL;
X509_STORE_CTX *x509_store_ctx = NULL;
OpenSSL_add_all_algorithms();
devCert = getDeviceCert(); // Returns X509 pointer
x509_store = X509_STORE_new();
X509_STORE_set_verify_cb(x509_store, verify_cb);
X509_STORE_set_flags(x509_store, 0);
x509_store_ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(x509_store_ctx, x509_store, devCert, NULL)
X509_STORE_CTX_set_purpose(x509_store_ctx, X509_PURPOSE_ANY);
ret = X509_verify_cert(x509_store_ctx);
if(x509_store_ctx != NULL) X509_STORE_CTX_free(x509_store_ctx);
if(x509_store != NULL) X509_STORE_free(x509_store);
if(devCert != NULL) X509_free(devCert);
EVP_cleanup();
return ret;
}
I didn't need to create any lookup methods. The key for me was looping through my certificates from my string in memory so I had all the certificates I need to complete the chain. The string is equivalent to what I would have fed into openssl verify for the option -CAfile.
Also, make sure your X509 pointers are not null when the are used.
A possible answer (don't have the rep points to add a comment, sorry): the manpage for SSL_CTX_load_verify_locations(3) says,
When building its own certificate chain, an OpenSSL client/server will try to fill in
missing certificates from CAfile/CApath, if the certificate chain was not explicitly
specified (see SSL_CTX_add_extra_chain_cert(3), SSL_CTX_use_certificate(3).
(Failure to match parens theirs, not mine.)
Which seems to mean that, as an alternative to SSL_CTX_load_verify_locations(3), it should be possible to use SSL_CTX_add_extra_chain_cert(3) or SSL_CTX_use_certificate(3) -- both of which take a X509 * arg. Thus obviating the need for Mr Ed's solution as seen above.
Please take a look at SSL_CTX_load_verify_locations () function: http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html
SSL_CTX_load_verify_locations() specifies the locations for ctx, at
which CA certificates for verification purposes are located. The
certificates available via CAfile and CApath are trusted.
You can generate a CA certificate file containing both ca.pem server.pem:
#!/bin/sh
rm CAfile.pem
for i in ca.pem server.pem ; do
openssl x509 -in $i -text >> CAfile.pem
done
And then set CAfile variable to point to CAfile.pem file.
Hope it helps !
See the official source code:
apps/verify.c
static int check(X509_STORE *ctx, const char *file,
STACK_OF(X509) *uchain, STACK_OF(X509) *tchain,
STACK_OF(X509_CRL) *crls, int show_chain);
You can see how to output 'OK' here:
if (i > 0 && X509_STORE_CTX_get_error(csc) == X509_V_OK) {
printf("%s: OK\n", (file == NULL) ? "stdin" : file);
The function dependence can be found in apps/apps.c

How to retrieve issuer alternative name for ssl certificate by openssl

I can get the subject alternative name like
X509_NAME_get_text_by_NID(X509_get_subject_name(x), NID_subject_alt_name, hc->https_domain_name, 256)
With same method by changing 2. parameter to NID_issuer_alt_name I am expecting to get issuer name like;
X509_NAME_get_text_by_NID(X509_get_subject_name(x), NID_issuer_alt_name, hc->https_ca_name, 256);
But instead I am getting a empty string . How can I retrieve issuer alternative name correctly?
You could try the following solution, as recommended in https://github.com/iSECPartners/ssl-conservatory :
static HostnameValidationResult matches_subject_alternative_name (const char *hostname, const X509 *server_cert) {
HostnameValidationResult result = MatchNotFound;
int i;
int san_names_nb = -1;
STACK_OF(GENERAL_NAME) *san_names = NULL;
// Try to extract the names within the SAN extension from the certificate
san_names = X509_get_ext_d2i((X509 *) server_cert, NID_subject_alt_name, NULL, NULL);
if (san_names == NULL) {
return NoSANPresent;
}
san_names_nb = sk_GENERAL_NAME_num(san_names);
// Check each name within the extension
for (i=0; i<san_names_nb; i++) {
const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i);
if (current_name->type == GEN_DNS) {
// Current name is a DNS name, let's check it
char *dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName);
// Make sure there isn't an embedded NUL character in the DNS name
if (ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) {
result = MalformedCertificate;
break;
}
else { // Compare expected hostname with the DNS name
if (strcasecmp(hostname, dns_name) == 0) {
result = MatchFound;
break;
}
}
}
}
sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
return result;
}
Hope it helps !
In your call to X509_NAME_get_text_by_NID with the NID_issuer_alt_name constant, I would have replaced X509_get_subject_name(x) by X509_get_issuer_name(x). I think this should do the trick you are after.

Read certificate files from memory instead of a file using OpenSSL

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
}
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);
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
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.
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;
}

x509 certificate verification in C

I do have certificates in DER and PEM format, my goal is to retrieve the fields of Issuer and Subject and verify the certificate with the CA public key and simultaneously verify CA certificate with the root public key.
I am able to retrieve all the details of issuer and subject but unable to verify the certificate.
The API used:
x509 = d2i_X509_fp (fp, &x509); //READING DER Format
x509 = PEM_read_X509 (fp, &x509, NULL, NULL); //READING PEM Format
//to retrieve the Subject:
X509_NAME_oneline(X509_get_subject_name(x509), subject, sizeof (subject));
//to retrieve the Issuer:
X509_NAME_oneline(X509_get_issuer_name(x509), issuer, sizeof (issuer));
//To store the CA public key (in unsigned char *key) that will be used to verify the
//certificate (in my case always sha1WithRSAEncryption):
RSA *x = X509_get_pubkey(x509)->pkey.rsa;
bn = x->n;
//extracts the bytes from public key & convert into unsigned char buffer
buf_len = (size_t) BN_num_bytes (bn);
stored_CA_pubKey = (unsigned char *)malloc (buf_len);
i_n = BN_bn2bin (bn, (unsigned char *)stored_CA_pubKey);
if (i_n != buf_len)
LOG(ERROR," : key error\n");
if (key[0] & 0x80)
LOG(DEBUG, "00\n");
stored_CA_pubKeyLen = EVP_PKEY_size(X509_get_pubkey(x509));
For Verification I went through different approaches but I am unable to verify:
a)
i_x509_verify = X509_verify(cert_x509, ca_pubkey);
b)
/* verify the signature */
int iRet1, iRet2, iReason;
iRet1 = EVP_VerifyInit(&md_ctx, EVP_sha1());
iRet2 = EVP_VerifyUpdate(&md_ctx, cert_code, cert_code_len);
rv = EVP_VerifyFinal(&md_ctx, (const unsigned char *)stored_CA_pubKey,
stored_CA_pubKeyLen, cert_pubkey);
NOTE : cert_code and stored_CA_pubKey are unsigned char buffers.
I use following code for verifying a certificate
init CertStore:
X509_STORE* m_store = X509_STORE_new();
X509_LOOKUP* m_lookup = X509_STORE_add_lookup(m_store,X509_LOOKUP_file());
X509_STORE_load_locations(m_store, "CAFile.pem", NULL);
X509_STORE_set_default_paths(m_store);
X509_LOOKUP_load_file(m_lookup,"CAFile.pem",X509_FILETYPE_PEM)
// alternative lookup by hashdir
// X509_LOOKUP* m_lookup=X509_STORE_add_lookup(m_store,X509_LOOKUP_hash_dir());
VerifyCert:
X509_STORE_CTX *storeCtx = X509_STORE_CTX_new();
X509_STORE_CTX_init(storeCtx,m_store,cert,NULL);
X509_STORE_CTX_set_flags(storeCtx, X509_V_FLAG_CB_ISSUER_CHECK);
if (X509_verify_cert(storeCtx) == 1)
{
printf("success");
}
else
{
printf("Verificatione rror: %s",X509_verify_cert_error_string(storeCtx->error));
}
X509_STORE_CTX_free(storeCtx);
you also need to cleanup m_store
if(m_store != NULL)
{
X509_STORE_free(m_store);
m_store = NULL;
}
Take a look at my self-answered question: https://stackoverflow.com/questions/3412032/openssl-c-how-do-you-verify-a-public-key-was-issued-by-your-private-ca it goes a long way to doing what you need.
X509_STORE* m_store = NULL;
X509_LOOKUP *m_lookup = NULL;
X509_STORE_CTX *storeCtx = NULL;
m_store = X509_STORE_new();
if(NULL == m_store) goto exit;
m_lookup = X509_STORE_add_lookup(m_store, X509_LOOKUP_file());
if(NULL == m_lookup) goto exit;
X509_STORE_load_locations(m_store, CA_CERT_PATH, NULL);
X509_STORE_set_default_paths(m_store);
X509_LOOKUP_load_file(m_lookup,CA_CERT_PATH, X509_FILETYPE_ASN1);
m_lookup = X509_STORE_add_lookup(m_store, X509_LOOKUP_hash_dir());
if(NULL == m_lookup) goto exit;
storeCtx = X509_STORE_CTX_new();
if(NULL == storeCtx) goto exit;
X509_STORE_CTX_init(storeCtx,m_store,cer_x509,NULL);
X509_STORE_CTX_set_flags(storeCtx, /*X509_V_FLAG_CHECK_SS_SIGNATURE*/0x4000);
if (X509_verify_cert(storeCtx) == 1)
{
printf("success\n");
}
else
{
printf("Verification error: %s\n",X509_verify_cert_error_string(storeCtx->error));
}
exit:
if(NULL != storeCtx) X509_STORE_CTX_free(storeCtx);
if(m_store != NULL)
{
X509_STORE_free(m_store);
m_store = NULL;
}
After Doing this also I am unable to verify Self signed certificate

Resources