I have a certificate chain in server:
Certificate chain
0 s:/******/O=Foobar International BV/OU****
i:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/****
1 s:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/****
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=**** - G5
2 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=**** - G5
i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
And my local root CA certificate is:
s:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/****
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=**** - G5
And I am using this snippet to verify the certificate:
//gcc -lssl -lcrypto -o certverify certverify.c
#include <openssl/ssl.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <openssl/conf.h>
#include <string.h>
int main() {
const char ca_bundlestr[] = "./ca-bundle.pem";
const char cert_filestr[] = "./cert-file.pem";
BIO *certbio = NULL;
BIO *outbio = NULL;
X509 *error_cert = NULL;
X509 *cert = NULL;
X509_NAME *certsubject = NULL;
X509_STORE *store = NULL;
X509_STORE_CTX *vrfy_ctx = NULL;
int ret;
/* ---------------------------------------------------------- *
* These function calls initialize openssl for correct work. *
* ---------------------------------------------------------- */
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
/* ---------------------------------------------------------- *
* Create the Input/Output BIO's. *
* ---------------------------------------------------------- */
certbio = BIO_new(BIO_s_file());
outbio = BIO_new_fp(stdout, BIO_NOCLOSE);
/* ---------------------------------------------------------- *
* Initialize the global certificate validation store object. *
* ---------------------------------------------------------- */
if (!(store=X509_STORE_new()))
BIO_printf(outbio, "Error creating X509_STORE_CTX object\n");
/* ---------------------------------------------------------- *
* Create the context structure for the validation operation. *
* ---------------------------------------------------------- */
vrfy_ctx = X509_STORE_CTX_new();
/* ---------------------------------------------------------- *
* Load the certificate and cacert chain from file (PEM). *
* ---------------------------------------------------------- */
ret = BIO_read_filename(certbio, cert_filestr);
if (! (cert = PEM_read_bio_X509(certbio, NULL, 0, NULL))) {
BIO_printf(outbio, "Error loading cert into memory\n");
exit(-1);
}
ret = X509_STORE_load_locations(store, ca_bundlestr, NULL);
if (ret != 1)
BIO_printf(outbio, "Error loading CA cert or chain file\n");
/* ---------------------------------------------------------- *
* Initialize the ctx structure for a verification operation: *
* Set the trusted cert store, the unvalidated cert, and any *
* potential certs that could be needed (here we set it NULL) *
* ---------------------------------------------------------- */
X509_STORE_CTX_init(vrfy_ctx, store, cert, NULL);
/* ---------------------------------------------------------- *
* Check the complete cert chain can be build and validated. *
* Returns 1 on success, 0 on verification failures, and -1 *
* for trouble with the ctx object (i.e. missing certificate) *
* ---------------------------------------------------------- */
ret = X509_verify_cert(vrfy_ctx);
BIO_printf(outbio, "Verification return code: %d\n", ret);
if(ret == 0 || ret == 1)
BIO_printf(outbio, "Verification result text: %s\n",
X509_verify_cert_error_string(vrfy_ctx->error));
/* ---------------------------------------------------------- *
* The error handling below shows how to get failure details *
* from the offending certificate. *
* ---------------------------------------------------------- */
if(ret == 0) {
/* get the offending certificate causing the failure */
error_cert = X509_STORE_CTX_get_current_cert(vrfy_ctx);
certsubject = X509_NAME_new();
certsubject = X509_get_subject_name(error_cert);
BIO_printf(outbio, "Verification failed cert:\n");
X509_NAME_print_ex(outbio, certsubject, 0, XN_FLAG_MULTILINE);
BIO_printf(outbio, "\n");
}
/* ---------------------------------------------------------- *
* Free up all structures *
* ---------------------------------------------------------- */
X509_STORE_CTX_free(vrfy_ctx);
X509_STORE_free(store);
X509_free(cert);
BIO_free_all(certbio);
BIO_free_all(outbio);
exit(0);
}
But this code return following output:
Verification return code: 0
Verification result text: unable to get issuer certificate
Verification failed cert:
countryName = US
organizationName = Symantec Corporation
organizationalUnitName = Symantec Trust Network
commonName = Symantec Class 3 Secure Server CA - G4
What's wrong here?
Your root CA uses probably the same public key as the first intermediate CA in chain (below the host certificate) and you have probably no root-CA which can be used to trust the last chain certificate. Such setups are not very common, but actually happen. Unfortunately OpenSSL has problems with this setup and will only try to verify the longest chain, even if a shorter chain provides already the necessary trust.
There is a bug entry for this OpenSSL problem, but nobody from the OpenSSL developers ever took care of it. You can also find a patch if you are looking for X509_V_FLAG_TRUSTED_FIRST. It looks like that OpenSSL 1.0.2 (not yet released) will have this option too.
From my understanding only OpenSSL has this kind of problem, i.e. neither NSS (Firefox, Chrome on Desktop) nor SChannel (Microsoft).