Failing to validate server certificate with OpenSSL - c++

I have written a SOAP client using OpenSSL (written in C++ on Ubuntu 12.04) but it currently works without checking the server security certificate. This is the function I am using to set up the connection and checking the certificate
bool bInitialiseSSL(SSL_CTX* &ctx, SSL* &ssl, BIO* &bio)
{
ctx = SSL_CTX_new(SSLv23_client_method());
bio = BIO_new_ssl_connect(ctx);
if (bio == NULL) {
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return false;
}
BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
char target[] = "api.betfair.com:https";
BIO_set_conn_hostname(bio, target);
BIO_set_nbio(bio,1);
while (1) {
if (BIO_do_connect(bio) <= 0) {
if (!BIO_should_retry(bio)) {
cout << "Connect failed." << endl;
BIO_free_all(bio);
SSL_CTX_free(ctx);
return false;
}
} else {
break;
}
}
if (BIO_do_handshake(bio) <= 0) {
BIO_free_all(bio);
SSL_CTX_free(ctx);
return false;
}
X509 *cert;
bool bValid = false;
cert = SSL_get_peer_certificate(ssl);
if ( cert != NULL ) {
long res = SSL_get_verify_result(ssl);
if (res == X509_V_OK) {
bValid = true;
} else {
cout << "Error in security validation: " << res << endl;
}
X509_free(cert);
}
return bValid;
}
This works fine but the return value of SSL_get_verify_result is 20 which corresponds to
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: unable to get local
issuer certificate
I have read some of the OpenSSL documentation for their functions but it is not particularly user friendly. I have looked at a number of web tutorials and I cannot see what I am doing wrong. My software worked perfectly before I tried to implement the certificate checking but I cannot see what I need to do. Do I need to configure settings on my machine? The server is betfair which is supposedly very secure and I find it hard to believe that they do not have valid SSL certificates. If anyone can tell me what I am doing wrong I would be very grateful.

It depends on the certificates of the server.
If it is a public valid certificate, you can include the CA certs file into SSL_CTX.
code:
ctx = SSL_CTX_new(SSLv23_client_method());
// You can load CA certs into SSL_CTX
SSL_CTX_load_verify_locations(ctx, cafile, NULL); // cafile: CA PEM certs file
You can download the public CA certs file from cURL website CA Certs from mozilla.org
If it is a private certs, and you have the certificate file, you can use SSL_CTX_use_certificate_file instead of SSL_CTX_load_verify_locations.

Related

Trying to make a secure handshake between server and client with OpenSSL

I'm trying to make a basic Client-Server program that can exchange messages using the OpenSSL library. I'm very new to OpenSSL and cryptography and I'm trying to understand exactly how to make sure that the connection between my client and server is secure. Currently I'm using self signed certificates for both client and server but the certificate verification fails when the client tries to connect to the server with this error:
140336190395008:error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed:../ssl/statem/statem_srvr.c:3711:
In the main method of my server program, I first set verify for the CTX and use the following flags:
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
Then when setting up the servlet I call the following method to show certificates:
void ShowCerts(SSL* ssl) /*show the ceritficates to client and match them*/ {
X509 *cert;
long int verify;
char *line;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
verify = SSL_get_verify_result(ssl);
if (verify == X509_V_OK) {
printf("Yay it worked\n");
}
if ( cert != NULL ) {
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Server: %s\n", line); /*server certifcates*/
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("client: %s\n", line); /*client certificates*/
free(line);
X509_free(cert);
} else {
printf("No certificates.\n");
}
}
As I said I'm very new to this so I might be missing something basic here. Also apologies in advance if I missed any important info, this is my first time asking a question here.

How to programmatically verify intermediate certificate signature with root public key?

Background
I have the following chain:
Root certificate, self signed
Intermediate certificate, issued by Root
User certificate, issued by Intermediate
I am trying to verify signature of intermediate certificate using public key of issuer (Root). I am using the following code piece to implement this task:
int verify_cert_sig(X509 *issuer, X509 *cert){
EVP_PKEY *public_key = X509_get_pubkey(issuer);
if(public_key == NULL){
_log->error("unable to get public key");
return ERROR;
}
int result = X509_verify(cert, public_key);
EVP_PKEY_free(public_key);
if(result <= 0){
return ERROR;
}
return SUCCESS;
}
int verify_sig(const std::string &issuer, const std::string &cert){
X509 *issuer_x509 = load_cert("root.pem", X509_FILETYPE_PEM);
if(issuer_x509 == NULL){
_log->error("unable to load x509 cert: {}", issuer);
return ERROR;
}
X509 *cert_x509 = load_cert("intermediate.pem", X509_FILETYPE_PEM);
if(cert_x509 == NULL){
_log->error("unable to load x509 cert: {}", cert);
return ERROR;
}
auto rv = verify_cert_sig(issuer_x509, cert_x509);
X509_free(issuer_x509);
X509_free(cert_x509);
if(rv <= 0){
return ERROR;
}
return SUCCESS;
}
Issue
For reasons unknown to me, the previous snippet will return error.
However, if I use openssl cli I can verify intermediate with root:
$:openssl verify -verbose -CAfile root.pem intermediate.pem
subroot.pem: OK
Questions
What am I doing wrong? Did I miss something?
How can I verify the signature of intermediate certificate using public key of issuer?

Setting SSL_CTX_set_cipher_list() fails with "No cipher match" error

I'm trying to limit the cypher list in my gsoap ssl server using SSL_CTX_set_cipher_list(). But the method returns with 0, no matter what list I provide. Without setting the list everything works fine.
I'm basically doing the same as in gsoap documentation https://www.genivia.com/doc/guide/html/group__group__ssl.html#ga3492465cdd8aa71fe746199d3842cac7
auto err = BIO_new_fp(stderr, BIO_NOCLOSE);
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
CalculatorSoapBindingService service;
service.soap->send_timeout = service.soap->recv_timeout = 5;
if (useSSL) {
soap_ssl_init(); // init SSL (just need to do this once in an application)
if (soap_ssl_server_context(
service.soap,
SOAP_SSL_REQUIRE_SERVER_AUTHENTICATION | SOAP_TLSv1 | SOAP_SSL_NO_DEFAULT_CA_PATH,
"server.pem", // server keyfile (cert+key)
"password", // password to read the private key in the keyfile
nullptr, // no cert to authenticate clients
nullptr, // no capath to trusted certificates
nullptr, // DH/RSA: use 2048 bit RSA (default with NULL)
nullptr, // no random data to seed randomness
"testServer" // no SSL session cache
))
{
service.soap_stream_fault(std::cerr);
exit(EXIT_FAILURE);
}
const char allowedCiphers[] = "ALL:!aNULL";
auto rc = SSL_CTX_set_cipher_list(service.soap->ctx, allowedCiphers);
if (rc != 1) {
ERR_print_errors(err);
exit(EXIT_FAILURE);
}
}
According to documentation return code 0 means complete failure.
The error message is: 140347788101304:error:1410D0B9:SSL routines:SSL_CTX_set_cipher_list:no cipher match:ssl_lib.c:1385:
When I run "openssl ciphers" I get the full list of ciphers.
Any ideas what I'm missing? Is the context not correctly initialized?
It was an SSL initialisation problem. Using SSL_library_init() instead of soap_ssl_init() solved the problem.
This is my final working solution:
CalculatorSoapBindingService service;
service.soap->send_timeout = service.soap->recv_timeout = 5; // 5 sec socket idle timeout
if (useSSL) {
SSL_library_init();
BIO_new_fp(stderr, BIO_NOCLOSE);
SSL_load_error_strings();
if (soap_ssl_server_context(
service.soap,
SOAP_SSL_REQUIRE_SERVER_AUTHENTICATION | SOAP_TLSv1 | SOAP_SSL_NO_DEFAULT_CA_PATH,
"server.pem", // server keyfile (cert+key)
"haslerrail", // password to read the private key in the keyfile
nullptr, // no cert to authenticate clients
nullptr, // no capath to trusted certificates
nullptr, // DH/RSA: use 2048 bit RSA (default with NULL)
nullptr, // no random data to seed randomness
"testServer" // no SSL session cache
))
{
service.soap_stream_fault(std::cerr);
exit(EXIT_FAILURE);
}
const char allowedCiphers[] = "ECDH:!aNULL:!eNULL:!ADH:!SHA:#STRENGTH";
auto nid = NID_X9_62_prime256v1;
auto key = EC_KEY_new_by_curve_name(nid);
if (key == nullptr) {
std::cout << "Failed to create curve" << OBJ_nid2sn(nid) << std::endl;;
exit(EXIT_FAILURE);;
}
SSL_CTX_set_tmp_ecdh(service.soap->ctx, key);
EC_KEY_free(key);
auto rc = SSL_CTX_set_cipher_list(service.soap->ctx, allowedCiphers);
if (rc != 1) {
std::cout << "no cipher list found " << rc << std::endl;
ERR_print_errors(err);
exit(EXIT_FAILURE);
}
}

Mutual authentication always succeeds with OpenSSL

I am using openssl and zmq to write a server and a client.
My client and server need mutual authentication.
but after I set SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL) on server, the handshake always successes whether the client send the certificate or not.
In addition, SSL_get_peer_certificate(tls->get_ssl_()) return null and SSL_get_verify_result(tls->get_ssl_()) return 0 which means X509_V_OK.
I am really confused and desperate now. Any suggestions or corrections?
This is part of my code:
OpenSSL_add_all_algorithms();
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
const SSL_METHOD *meth;
SSL_CTX *ssl_ctx;
//**************************part of client************************
{
meth = SSLv23_client_method();
ssl_ctx = SSL_CTX_new(meth);
SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,NULL);
int rc1 = SSL_CTX_load_verify_locations(ssl_ctx, ".\\demoCA\\private\\server_chain.pem",".\\demoCA\\private\\");///
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx,"pw");
std::string cert_chain(".\\demoCA\\private\\client_chain.pem");
std::string cert(".\\demoCA\\private\\client_crt.pem");
std::string key(".\\demoCA\\private\\client_key.pem");
int code = SSL_CTX_use_certificate_chain_file(ssl_ctx,cert_chain.c_str());
if (code != 1)
{
std::cout<<"error1\n";
//throw TLSException("failed to read credentials.");
}
code = SSL_CTX_use_PrivateKey_file(ssl_ctx,key.c_str(),SSL_FILETYPE_PEM);
i f (code != 1)
{
std::cout<<"error2\n";
//throw TLSException("failed to read credentials.");
}
if(!SSL_CTX_check_private_key(ssl_ctx))
{
std::cout<<"key wrong";
system("pause");
exit(0);
}
}
//*****************part of server****************************
{
meth = SSLv23_server_method();
ssl_ctx = SSL_CTX_new(meth);
SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL)
SSL_CTX_set_client_CA_list(ssl_ctx,SSL_load_client_CA_file(".\\demoCA\\private\\client_chain.pem"));//
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx,"pw");
std::string cert_chain(".\\demoCA\\private\\server_chain.pem");
std::string cert(".\\demoCA\\private\\server_crt.pem");
std::string key(".\\demoCA\\private\\server_key.pem");
int rc = SSL_CTX_use_certificate_file(ssl_ctx,cert.c_str(),SSL_FILETYPE_PEM);
if (rc!=1)
{
//throw TLSException("failed to read credentials.");
std::cout<<"error1\n";
}
rc = SSL_CTX_use_PrivateKey_file(ssl_ctx,key.c_str(),SSL_FILETYPE_PEM);
if (rc!=1)
{
//throw TLSException("failed to read credentials.");
std::cout<<"error2\n";
}
int rcode = SSL_CTX_check_private_key(ssl_ctx);
if(rcode!=1)
{
std::cout<<"key wrong";
system("pause");
//exit(0);
}
}
From the documentation of SSL_CTX_set_verify:
SSL_VERIFY_FAIL_IF_NO_PEER_CERT
Server mode: if the client did not return a certificate, the TLS/SSL handshake is immediately terminated with a "handshake failure" alert. This flag must be used together with SSL_VERIFY_PEER.
You did not use it together with SSL_VERIFY_PEER as described in the documentation and thus it has no effect.

openssl SSL_get_verify_result returns error 20

I am writing a C++ program that connects using SSL. The certificate chain checks out using:
openssl verify -CAfile test.pem private.pem
where test.pem contains the intermediate and root certificate. My test program does not verify the certificate chain.
if ( !SSL_CTX_load_verify_locations( ctx, "c:/Certs/test.pem", NULL ) ) {
// Failure message and cleanup goes here.
}
SSL* ssl;
BIO* bio = BIO_new_ssl_connect( ctx );
BIO_get_ssl( bio, &ssl );
SSL_set_mode( ssl, SSL_MODE_AUTO_RETRY );
BIO_set_conn_hostname( bio, "url.com:https" );
if ( BIO_do_connect( bio ) <= 0 ) {
// Failure message and cleanup goes here.
}
if ( SSL_get_verify_result( ssl ) != X509_V_OK ){
// Here is where I get the error 20...
// Free all resources and exit.
}
OpenSSL documentation describes error 20 as:
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: unable to get local issuer certificate.
The issuer certificate could not be found: this occurs if the issuer certificate of an
untrusted certificate cannot be found.
I need help identifying the problem and how to solve it. I am certain the certificates I have are correct.
It seems the certificate or certificate chain is not trusted.
You can load your own from a pem file before trying to connect by using:
int rc = SSL_CTX_load_verify_locations(ssl_context, file_name, NULL);
if (rc != 1) { // verify authentication result
g_warning("Load of certificates failed!: %s", X509_verify_cert_error_string(ERR_get_error()));
return FALSE;
}
Additionally you can load from memory directly.
With something like this:
char *chain_certs = "------- BEGIN CERTIFICAT...."; /// <<< YOUR CERTIFICATE CHAIN
// Load chain of certs
X509 *cacert=NULL;
BIO *mem = BIO_new_mem_buf(chain_certs,strlen(chain_certs));
X509_STORE *cert_store = SSL_CTX_get_cert_store(ssl_context);
if(cert_store!=NULL){
int index = 0;
while ((cacert = PEM_read_bio_X509(mem, NULL, 0, NULL))!=NULL) {
if(cacert) {
g_debug("Our certificate name is %s", cacert->name);
X509_STORE_add_cert(cert_store, cacert);
X509_free(cacert);
cacert=NULL;
} /* Free immediately */
index++;
}
}
BIO_free(mem);