Read certificate files from memory instead of a file using OpenSSL - c

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

Related

Get entire X509 certificate chain using OpenSSL

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.

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.

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

WinVerifyTrust to check for a specific signature?

I'm implementing a process elevation helper for Windows. It's a program that will run in elevated mode and launch other programs with administrator privileges without displaying additional UAC prompts. For security reasons, I want to make sure only binaries that are digitally signed with my company's Authenticode key can be executed.
The WinVerifyTrust function gets me halfway there, but it only ensures that a binary is signed by some key that is part of Microsoft's chain of trust. Is there a relatively simple way to perform the Authenticode verification AND ensure that it is signed by our private key?
I believe what you're looking for is CryptQueryObject.
With it you should be able to pull the involved certificate out of a PE, and do any additional checks you want.
By way of example, this will get you to a HCRYPTMSG. From there you can use CryptMsgGetParam to pull out whatever you want. I'd hoped to make something more 'robust', but these APIs are pretty hairy insomuch as they require a lot of branching to handle all their return cases.
So, here's a p/invoke-rific c# example (I started in C, but that was basically unreadable):
static class Crypt32
{
//Omitting flag constants; you can look these up in WinCrypt.h
[DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptQueryObject(
int dwObjectType,
IntPtr pvObject,
int dwExpectedContentTypeFlags,
int dwExpectedFormatTypeFlags,
int dwFlags,
out int pdwMsgAndCertEncodingType,
out int pdwContentType,
out int pdwFormatType,
ref IntPtr phCertStore,
ref IntPtr phMsg,
ref IntPtr ppvContext);
}
class Program
{
static void Main(string[] args)
{
//Path to executable here
// I tested with MS-Office .exe's
string path = "";
int contentType;
int formatType;
int ignored;
IntPtr context = IntPtr.Zero;
IntPtr pIgnored = IntPtr.Zero;
IntPtr cryptMsg = IntPtr.Zero;
if (!Crypt32.CryptQueryObject(
Crypt32.CERT_QUERY_OBJECT_FILE,
Marshal.StringToHGlobalUni(path),
Crypt32.CERT_QUERY_CONTENT_FLAG_ALL,
Crypt32.CERT_QUERY_FORMAT_FLAG_ALL,
0,
out ignored,
out contentType,
out formatType,
ref pIgnored,
ref cryptMsg,
ref context))
{
int error = Marshal.GetLastWin32Error();
Console.WriteLine((new Win32Exception(error)).Message);
return;
}
//expecting '10'; CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED
Console.WriteLine("Context Type: " + contentType);
//Which implies this is set
Console.WriteLine("Crypt Msg: " + cryptMsg.ToInt32());
return;
}
To get the certificate information from signed code use this:
using System.Security.Cryptography.X509Certificates;
X509Certificate basicSigner = X509Certificate.CreateFromSignedFile(filename);
X509Certificate2 cert = new X509Certificate2(basicSigner);
Then you can get the cert details like this:
Console.WriteLine(cert.IssuerName.Name);
Console.WriteLine(cert.SubjectName.Name);
// etc
these are some of the nastiest APIs I've ever worked with
A word of warning: it's worse than you already thought.
At least since introducing SHA-256 signing (has this always been the case?), it's possible for Authenticode to have multiple signatures. They're not encoded as multiple signatures in the PKCS-7 signature message; instead, they're unauthenticated message attributes of type OID_NESTED_SIGNATURE, each containing another complete PKCS-7 signature message.
WinVerifyTrust will tell you the file is valid if any of the signatures are valid and come from a trusted certificate chain. However it won't tell you which of the signatures was valid. If you then use CryptQueryObject to read the full PKCS-7 message, and only look at the certificate for the primary signature (as in the code samples here and on MSDN), you're not necessarily looking at a verified certificate. The associated signature might not match the executable, and/or the certificate might not have a trusted CA chain.
If you're using the details of the primary signature to validate that the certificate is one your software trusts, you're vulnerable to a situation where WinVerifyTrust is trusting a secondary signature, but your code is checking the primary signature's certificate is what you expected, and you haven't noticed that the signature from the primary certificate is nonsense. An attacker could use your public certificate without owning its private key, combined with some other code-signing certificate issued to someone else, to bypass a publisher check this way.
From Win8 onwards, WinVerifyTrust can optionally validate specific signatures, so you should be able to iterate the signatures to find one that is valid and one that satisfies your requirements.
If you have to be Win7-compatible, though, as far as I know the best you can manage is MsiGetFileSignatureInformation. From experimentation (as for everything else here, the actual documentation is frustratingly woolly), it seems to return the trusted certificate when WinVerifyTrust trusts one. But if there isn't a trusted signature, it returns the primary signature's certificate anyway, so you still have to use WinVerifyTrust to check that first.
Of course there also plenty of possible time-of-check/time-of-use problems here.
found the solution here:
http://www.ucosoft.com/how-to-program-to-retrieve-the-authenticode-information.html
here it is with indentation:
#define _UNICODE 1
#define UNICODE 1
#include <windows.h>
#include <tchar.h>
#include <wincrypt.h>
#include <Softpub.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment (lib, "Crypt32")
// the Authenticode Signature is encode in PKCS7
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
// Information structure of authenticode sign
typedef struct
{
LPWSTR lpszProgramName;
LPWSTR lpszPublisherLink;
LPWSTR lpszMoreInfoLink;
DWORD cbSerialSize;
LPBYTE lpSerialNumber;
LPTSTR lpszIssuerName;
LPTSTR lpszSubjectName;
}
SPROG_SIGNATUREINFO, *PSPROG_SIGNATUREINFO;
VOID GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo);
VOID GetCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo);
BOOL GetAuthenticodeInformation(LPCTSTR lpszFileName, PSPROG_SIGNATUREINFO pInfo)
{
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
DWORD dwSignerInfo;
BOOL bRet = FALSE;
__try
{
// as CryptQueryObject() only accept WCHAR file name, convert first
WCHAR wszFileName[MAX_PATH];
#ifdef UNICODE
if ( !lstrcpynW( wszFileName, lpszFileName, MAX_PATH))
__leave;
#else
if ( mbstowcs( wszFileName, lpszFileName, MAX_PATH) == -1)
__leave;
#endif
//Retrieve the Message Handle and Store Handle
DWORD dwEncoding, dwContentType, dwFormatType;
if ( !CryptQueryObject( CERT_QUERY_OBJECT_FILE, wszFileName,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY, 0, &dwEncoding,
&dwContentType, &dwFormatType, &hStore,
&hMsg, NULL))
__leave;
//Get the length of SignerInfo
if ( !CryptMsgGetParam( hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo))
__leave;
// allocate the memory for SignerInfo
if ( !(pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc( LPTR, dwSignerInfo)))
__leave;
// get the SignerInfo
if ( !CryptMsgGetParam( hMsg, CMSG_SIGNER_INFO_PARAM, 0, (PVOID)pSignerInfo, &dwSignerInfo))
__leave;
//get the Publisher from SignerInfo
GetProgAndPublisherInfo( pSignerInfo, pInfo);
//get the Certificate from SignerInfo
GetCertificateInfo( hStore, pSignerInfo, pInfo);
bRet = TRUE;
}
__finally
{
// release the memory
if (pSignerInfo != NULL) LocalFree(pSignerInfo);
if (hStore != NULL) CertCloseStore(hStore, 0);
if (hMsg != NULL) CryptMsgClose(hMsg);
}
return bRet;
}
LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
{
LPWSTR outputString = NULL;
// allocate the memory
outputString = (LPWSTR)VirtualAlloc(NULL, (wcslen(inputString) + 1) * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE);
// copy
if (outputString != NULL)
{
lstrcpyW(outputString, inputString);
}
return outputString;
}
VOID GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo)
{
PSPC_SP_OPUS_INFO OpusInfo = NULL;
DWORD dwData;
__try
{
// query SPC_SP_OPUS_INFO_OBJID OID in Authenticated Attributes
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// get the length of SPC_SP_OPUS_INFO
if ( !CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwData))
__leave;
// allocate the memory for SPC_SP_OPUS_INFO
if ( !(OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData)))
__leave;
// get SPC_SP_OPUS_INFO structure
if ( !CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
OpusInfo,
&dwData))
__leave;
// copy the Program Name of SPC_SP_OPUS_INFO to the return variable
if (OpusInfo->pwszProgramName)
{
pInfo->lpszProgramName = AllocateAndCopyWideString(OpusInfo->pwszProgramName);
}
else
pInfo->lpszProgramName = NULL;
// copy the Publisher Info of SPC_SP_OPUS_INFO to the return variable
if (OpusInfo->pPublisherInfo)
{
switch (OpusInfo->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
pInfo->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
pInfo->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
break;
default:
pInfo->lpszPublisherLink = NULL;
break;
}
}
else
{
pInfo->lpszPublisherLink = NULL;
}
// copy the More Info of SPC_SP_OPUS_INFO to the return variable
if (OpusInfo->pMoreInfo)
{
switch (OpusInfo->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
pInfo->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
pInfo->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
break;
default:
pInfo->lpszMoreInfoLink = NULL;
break;
}
}
else
{
pInfo->lpszMoreInfoLink = NULL;
}
break; // we have got the information, break
}
}
}
__finally
{
if (OpusInfo != NULL) LocalFree(OpusInfo);
}
}
VOID GetCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo)
{
PCCERT_CONTEXT pCertContext = NULL;
__try
{
CERT_INFO CertInfo;
DWORD dwData;
// query Signer Certificate in Certificate Store
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
if ( !(pCertContext = CertFindCertificateInStore( hStore,
ENCODING, 0, CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo, NULL)))
__leave;
dwData = pCertContext->pCertInfo->SerialNumber.cbData;
// SPROG_SIGNATUREINFO.cbSerialSize
pInfo->cbSerialSize = dwData;
// SPROG_SIGNATUREINFO.lpSerialNumber
pInfo->lpSerialNumber = (LPBYTE)VirtualAlloc(NULL, dwData, MEM_COMMIT, PAGE_READWRITE);
memcpy( pInfo->lpSerialNumber, pCertContext->pCertInfo->SerialNumber.pbData, dwData);
// SPROG_SIGNATUREINFO.lpszIssuerName
__try
{
// get the length of Issuer Name
if (!(dwData = CertGetNameString( pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG, NULL, NULL, 0)))
__leave;
// allocate the memory
if ( !(pInfo->lpszIssuerName = (LPTSTR)VirtualAlloc(NULL, dwData * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE)))
__leave;
// get Issuer Name
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG, NULL, pInfo->
lpszIssuerName, dwData)))
__leave;
}
__finally
{
}
// SPROG_SIGNATUREINFO.lpszSubjectName
__try
{
//get the length of Subject Name
if (!(dwData = CertGetNameString( pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0)))
__leave;
// allocate the memory
if ( !(pInfo->lpszSubjectName = (LPTSTR)VirtualAlloc(NULL, dwData * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE)))
__leave;
// get Subject Name
if (!(CertGetNameString( pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, pInfo->lpszSubjectName, dwData)))
__leave;
}
__finally
{
}
}
__finally
{
if (pCertContext != NULL)
CertFreeCertificateContext(pCertContext);
}
}
int _tmain(int argc, TCHAR *argv[])
{
if (argc != 2)
{
_tprintf(_T("Usage: SignedFileInfo \n"));
return 0;
}
else
{
SPROG_SIGNATUREINFO SignInfo;
ZeroMemory(&SignInfo, sizeof(SignInfo));
GetAuthenticodeInformation( argv[1], &SignInfo);
wprintf(L"Program Name: %s\n", SignInfo.lpszProgramName);
wprintf(L"Publisher Link: %s\n", SignInfo.lpszPublisherLink);
wprintf(L"More Info Link: %s\n", SignInfo.lpszMoreInfoLink);
{
_tprintf(_T("Serial Number: "));
DWORD dwData = SignInfo.cbSerialSize;
for (DWORD n = 0; n < dwData; n++)
{
_tprintf(_T("%02x "),
SignInfo.lpSerialNumber[dwData - (n + 1)]);
}
_tprintf(_T("\n"));
}
_tprintf(_T("Issuer Name: %s\n"), SignInfo.lpszIssuerName);
_tprintf(_T("Subject Name: %s\n"), SignInfo.lpszSubjectName);
if ( SignInfo.lpszProgramName) VirtualFree(SignInfo.lpszProgramName, 0, MEM_RELEASE);
if ( SignInfo.lpszPublisherLink) VirtualFree(SignInfo.lpszPublisherLink, 0, MEM_RELEASE);
if ( SignInfo.lpszMoreInfoLink) VirtualFree(SignInfo.lpszMoreInfoLink, 0, MEM_RELEASE);
if ( SignInfo.lpSerialNumber) VirtualFree(SignInfo.lpSerialNumber, 0, MEM_RELEASE);
if ( SignInfo.lpszIssuerName) VirtualFree(SignInfo.lpszIssuerName, 0, MEM_RELEASE);
if ( SignInfo.lpszSubjectName) VirtualFree(SignInfo.lpszSubjectName, 0, MEM_RELEASE);
return 0;
}
}

Resources