Topic: Verifying SSL Client Certificates?
I am writing an application that uses libwebsockets with CyaSSL/wolfSSL as SSL provider.
My application uses a selfsigned CA certificate and server certificate, both created with OpenSSL.
So far, so good. I even get a green lock in the navigation bar when I import the CA certificate into chrome and connect to the websocket.
But now I would like users connecting to the websocket to have to present a client certificate to be able to connect. Initially I thought that the code I was using works. Even blocking revoked client certificates.
Recently, I have discovered that any client certificate signed by an 'old' version of the CA certificate authenticates as valid when it should fail authentication, so the code only seems to work!
I've been staring myself blind on any documentation I can find on this subject, but so far I still haven't figured it out.
Can you SSL guru's please help me, or point me in the right direction?
The initialisation of the SSL library is done by libwebsockets, any 'extra' autentication like loading a certificate revocation list is done through a callback function. This is the relevant code for the libwebsockets initialization and the callback function for the html-protocol:
#include <wolfssl/openssl/ssl.h>
static struct libwebsocket_context * websocket_context;
static char *fn_ca_cert; // complete path to CA certificate
static char *fn_srv_cert; // complete path to Server certificate
static char *fn_srv_key; // complete path to Server private key
static char *fn_crl_cert; // complete path to CRL certificate
static char *path_crl_cert; // complete path to CRL directory (ie. fn_crl_cert without the filename)
bool init_libwebsockets( void )
{
struct lws_context_creation_info context_info;
memset( &context_info, 0, sizeof context_info );
...
...
context_info.ssl_cert_filepath = fn_srv_cert;
context_info.ssl_private_key_filepath = fn_srv_key;
context_info.options = LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
...
websocket_context = libwebsocket_create_context( &context_info );
if( websocket_context == NULL ){
SYSLOG( LOG_ERR, "websocket: init failed\n" );
return false;
}
return true;
}
static int websocket_callback_http( struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len ){
{
int n;
switch( reason ){
...
...
// This callback loads the CRL
// user = SSL_CTX*
//
case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
#if ! HAVE_NO_SSL
{
char errbuf[160];
SSL_CTX *ctx = (SSL_CTX*) user;
// Load the CA cert
n = SSL_CTX_load_verify_locations( ctx, fn_ca_cert, NULL );
if( n != 1 ){
n = ERR_get_error();
SYSLOG( LOG_ERR, "Encryption: problem loading CA certificate: %s", ERR_error_string( n, errbuf ) );
return 1;
}
// Make sure our CRL exists
FILE *fp = fopen( fn_crl_cert, "rt" );
if( fp ){
fclose( fp );
// Load the CRL and activate CRL checking
n = wolfSSL_CTX_LoadCRL( ctx, path_crl_cert, SSL_FILETYPE_PEM, 0 );
if( n != 1 ){
SYSLOG( LOG_ERR, "Encryption: problem loading CRL directory: %s", wolfSSL_ERR_error_string( n, errbuf ) );
return 1;
}
n = wolfSSL_CTX_EnableCRL( ctx, 0 );
if( n != 1 ){
SYSLOG( LOG_ERR, "Encryption: problem enabling CRL: %s", wolfSSL_ERR_error_string( n, errbuf ) );
return 1;
}
}
else {
SYSLOG( LOG_ERR, "Encryption: problem loading CRL: %s", fn_crl_cert );
return 1;
}
}
#endif
break;
// This callback decides whether or not to allow a client to connect (for all
// protocols) and is called as:
// int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx);
//
// in = SSL*
// user = X509_STORE_CTX*
// len = preverify_ok
//
// Libwebsocket return conventions apply as usual, ie. return nonzero to block
// the connection.
//
case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
{
n = 0;
#if ! HAVE_NO_SSL
int err = 0;
char errbuf[256];
if( !len ){
// The client certificate failed to be verified,
// log the error message and refuse the connection by returning nonzero.
//
err = X509_STORE_CTX_get_error( (X509_STORE_CTX*)user );
int depth = X509_STORE_CTX_get_error_depth( (X509_STORE_CTX*)user );
wolfSSL_ERR_error_string_n( err, errbuf, sizeof( errbuf ) );
SYSLOG( LOG_ERR, "Encryption: Error: %s (%d), depth: %d", errbuf, err, depth );
n = 1;
}
#endif
}
return n;
...
...
default:
break;
}
return 0;
}