Another situation where I get X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY - ssl

As a followup to this original question:
Unable to get certificate locally
I did solve the original problem as jww answered.
And I have now followed the same steps of importing the certificate chain for our company site, from "comodo". I simply added them to the file which I originally used with the "google.com" root certs.
Now, although this still works fine with "google", when I connect to our company website, I still get the error code 20 on the SSL_get_verify_result() call.
Is this a result of our using a "wildcard" certificate? i.e.: *.domain.com.
The version of OpenSSL I am currently using is 1.0.1g.
I don't see any other differences from my perspective.
Thanks for any advice.
----- Updated ------
First, let me comment that I am not mentioning our domain, and not posting too much material from the OpenSSL command, as I am not familiar enough with what should be kept confidential.
What I did was combine the base64 encoded certificates into one big file, as the previous post instructed. And I obtained them via the browser "export" utility in the same manner for both. That means the certificates that we use, as well as the google certificates from my previous post are all concatenated. Specifically it looks like this now:
Our Company Cert
-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----
and it is signed by these guys - ComodoRSA
-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----
and that is signed at the root here - ComodoRoot
-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----
and this is the GOOGLE G3 who signed the "www.google.com"
-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----
and the GOOGLE G3 is signed by this one - globalSign
-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----
Then the code segment I use to test looks like this:
strcpy(host,"our.domain.com");
// strcpy(host,"www.google.com");
/* Build our SSL context*/
ctx = initialize_ctx(KEYFILE,NULL);
/* Connect the TCP socket*/
sock = tcp_connect(host,port);
And then later...
result = SSL_get_verify_result(ssl);
switch(result) {
case X509_V_ERR_CERT_HAS_EXPIRED : break;
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN : break;
case X509_V_OK : break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT : break;
default :puts("Certificate doesn't verify");
}
Simply put, this same code, using this same CRT file, does not give me a "20" error when I use the www.google.com" host, but does give me an error "20" when I use our server. The extent of the test involves changing that commented out name of the host.
Connections to the HTTPS server with commercial clients (Chrome, IE, FF...) have no errors.
As for the comment that recommended the command, I get the following (hopefully I pasted the necessary information):
For Google:
depth=2 OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign verify return:1
depth=1 C = US, O = Google Trust Services, CN = Google Internet Authority G3
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = www.google.com
verify return:1
read:errno=0
---- other stuff ----
Certificate chain
0 s:/C=US/ST=California/L=Mountain View/O=Google LLC/CN=www.google.com
i:/C=US/O=Google Trust Services/CN=Google Internet Authority G3
1 s:/C=US/O=Google Trust Services/CN=Google Internet Authority G3
i:/OU=GlobalSign Root CA - R2/O=GlobalSign/CN=GlobalSign
---
And for our domain, I am getting the following (company specifics hidden):
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Certification Authority
verify error:num=20:unable to get local issuer certificate
verify return:0
read:errno=10054
---- other stuff ---
Certificate chain
0 s:/C=US/postalCode=00000/ST=IL/L=city/street=main/O=company./OU=PremiumSSL Wildcard/CN=*.domain.com
i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
----
Now, the chain is that our certificate is signed by "ComodoRSA", and that it is signed by "ComodoRoot".
However, as I originally indicated, our certificate is a "wildcard" certificate, where the google certificate isn't.
So that was my question: Is there a problem using wild card certificates with the version of openSSL 1.0.1g?
--- EDIT2 ----
I am adding more content to the post, so I can include an image from the browser.
Our certificate is on a live web site, and is not self signed.
I am checking the common name in a portion of the code not shown. In this post I am only hoping for advice on this error.
I found a site that uses the same chain we use: DrudgeReport.com
I simply extracted the certificates with the browser, and saved them into a file. This was identical to the steps I used form the google.com site. (View certificate and copy to file)
The result from Drudge is error 19 which is "self signed", not error 20 which was my error. The root level (comodo secure) is the same when I copy to file from either site (as I would expect).
As I learn this from your comments and the link, I see the next intermediate question is: how do I know what certificates are being sent by the server, and which were in the store? Then I would know which to import. What, if any, is the indicator from the openssl command output you provided?
The educational material on openssl and this is rather difficult to find. Just a lot of doc, which to a novice such as myself, is rather circular in it's definitions.
Thanks for your feedback so far.

So that was my question: Is there a problem using wild card certificates with the version of openSSL 1.0.1g?
It is not a problem of wildcard certificates. In fact, your code does not even check the subject of the certificate at all (i.e. your code is insecure), it mostly checks the certificate chain, expiration and purpose of the certificate. And the error message from openssl s_client clearly points out the problem:
verify error:num=20:unable to get local issuer certificate
Thus, the problem is not the certificate subject but that it cannot find a local trust anchor. Looking at the certificate chain provided by your server gives the following certificate chain:
[1] CN=*.domain.com, issued by [2]
[2] CN=COMODO RSA Organization Validation Secure Server CA, issued by [3]
[3] CN=COMODO RSA Certification Authority, issued by [ROOT]
The expected [ROOT] is "CN=AddTrust External CA Root" - only this CA is not in your list of trusted root CA.
While you give not the detailed names of the certificates you have in your local trust store my guess is that the CA you call "ComodoRoot" is similar to what I have as "[3] CN=COMODO RSA Certification Authority" in the list. Only, in your trust store it is likely the self-signed version of the certificate while in the certificate chain provided by the server it is a certificate issued by "[ROOT] CN=AddTrust External CA Root". Both certificates have the same public and private key which means that the signatures in certificate chain can be successfully validated with both.
But, the old (and long unsupported version) of OpenSSL you are using can not properly deal with this kind of situation. It will follow the certificate chain send by the server and then expect the last certificate in the chain signed by on of the certificates in your trust store. If this fails it will not check if a shorter chain might be validated successfully instead.
This means it will succeed if you either have "CN=AddTrust External CA Root" in your trust store or if the server sends a short chain which ends with "[2] CN=COMODO RSA Organization Validation Secure Server CA" since then it will find the issuer for this (your "ComodoRoot", i.e. "CN=COMODO RSA Certification Authority") in your trust store.
For a more detailed explanation of this problem see this answer at stackoverflow.com or this article. Note that there is no way to fix this in code with OpenSSL 1.0.1 - you need to either add the missing certificate to your trust store or make changes to the certificate chain send by the server.

Related

Get complete certificate chain including the root certificate

How do I get complete certificate chain for a server? Though some claim one should be able to do just that with openssl s_client -showcerts, it turns not always to be the case.
echo | openssl s_client -CApath /etc/ssl/certs -connect www.ssllabs.com:443 \
-showcerts | grep -B2 BEGIN
depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Domain Validation Secure Server CA
verify return:1
depth=0 OU = Domain Control Validated, OU = PositiveSSL, CN = www.ssllabs.com
verify return:1
0 s:/OU=Domain Control Validated/OU=PositiveSSL/CN=www.ssllabs.com
i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
-----BEGIN CERTIFICATE-----
--
1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
-----BEGIN CERTIFICATE-----
--
2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
-----BEGIN CERTIFICATE-----
DONE
Here we have three certificates our of four. All except of the AddTrust External CA Root certificate. (Possibly because it is not included into the certificate bundle. And not like this is required. And yes, I can find the missing one at /etc/ssl/certs)
How do I get all certificates for a server in a fully automatic fashion?
Meta: I tried to answer this in superuser but you deleted it. Fortunately when I found this copy most of my work was still sitting in a scratch notepad I hadn't closed, otherwise I wouldn't have been willing to do the research work twice.
s_client -showcerts shows the certs sent by the server; according to the RFCs, this should be a valid chain in upward order except that the root MAY (in RFC2119 definition i.e. allowed but not particularly recommended) be omitted. However, not all servers are configured correctly, and some may send extra, missing, and/or out-of-order certs. Also depending on the CA used there may be more than one valid chain but the server can only send one. openssl currently will use only the chain sent, but this will change soon in 1.0.2, and other reliers already sometimes find a different chain than the one sent.
openssl: if the received chain is complete up to and maybe including a root which is in the truststore used (whose default location depends on system or build, and in any case can always be overridden) then openssl client will validate it as okay -- unless it is expired, or revoked and that info is available which usually it isn't. In that case you can write a client program that connects after setting a cert-verify callback function that outputs the full certs as processed by the validation loop, or other info from them you want, as opposed to s_client which uses a callback that logs (only) the subject name in the depth=n lines, which you can see in your example includes all 4 certs in the chain here. openssl is opensource, so a client program that does things mostly like s_client could be a modified copy of s_client (in this case specifically s_cb.c).
Java can also do this and is a good bit shorter to write, but requires Java be installed. If the received chain validates against an anchor in the truststore used (which defaults to a set of public roots but can be modified or overridden, and can have non-root anchors) you similarly can write a program (maybe 20 lines) to connect using a HandshakeCompletedListener which dumps the info from event.getPeerCertificates(). However if the chain doesn't validate, Java aborts the handshake with an exception and you get no certs at all, unlike the openssl case where you might get partial information before the error occurs -- plus openssl's checking, at least by default, isn't quite as strict anyway.
UPDATE: for completeness, in Java 7+, commandline keytool -printcert -sslserver displays the chain sent, in a rather cluttered format.
Among the browsers I can easily check, Firefox and Chrome on Windows (at least) can write out the chain they found and validated. ISTR but can't easily retest the Firefox error/exception dialog can also do this for a chain that fails to validate and may be incomplete. These are not automatic as-is, but I've seen ads for numerous "simulate GUI user input" tools that apparently could drive them as desired.
You get the chain including the builtin trusted root certificate inside the verify_callback (see SSL_CTX_set_verify. With a small Perl program you can dump the chain like this:
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::SSL;
IO::Socket::SSL->new(
PeerHost => 'www.google.com:443',
SSL_verify_callback => sub {
my $cert = $_[4];
my $subject = Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_subject_name($cert));
my $issuer = Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_issuer_name($cert));
print "# $subject (issuer=$issuer)\n";
print Net::SSLeay::PEM_get_string_X509($cert),"\n";
return 1;
}
) or die $SSL_ERROR||$!;

SSL site and browser warning [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have an e commerce site naturalvape.us using wordpress. hostgator is where its hosted.
I have a ssl certification installed. My issue my site shows a caution sign and says it is unsecured. The guys at hostgator gave me a link to this site to force ssl/https. Apparently some type of code is needed to redirect to the secure site.
Why do I have a warning in the browser?
How can I redirect the site to https?
To expand upon Boan's comment, you certificate chain is malformed.
You are only sending the end entity (server) certificate; and you need to send both the end entity certificate and two intermediate certificates required for Comodo.
You need to send the intermediate certificates to avoid the "which directory" problem. Its a well known problem in PKI. That's what clients are experiencing - they don't know where to go to get the missing intermediate certificate.
Here's how you can check for it:
$ openssl s_client -connect naturalvape.us:443 -showcerts
CONNECTED(00000003)
depth=0 OU = Domain Control Validated, OU = PositiveSSL, CN = naturalvape.us
verify error:num=20:unable to get local issuer certificate
verify return:1
...
---
Certificate chain
0 s:/OU=Domain Control Validated/OU=PositiveSSL/CN=naturalvape.us
i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
-----BEGIN CERTIFICATE-----
MIIFUDCCBDigAwIBAgIRAOIeCA8uCx0hLc8AQSHiak8wDQYJKoZIhvcNAQELBQAw
gZAxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
...
t3d8prtVxlUd9xp0AEXPOLI1uKQlDKNCOQlHFrINkZbwwg6hmomiFXx5IpfVSb9U
XIqr/cZP7xtD2oiYCJ2giJ7dHLU=
-----END CERTIFICATE-----
---
Server certificate
subject=/OU=Domain Control Validated/OU=PositiveSSL/CN=naturalvape.us
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
---
No client certificate CA names sent
---
...
Notice there's one certificate present with a subject of CN=naturalvape.us (subject is the "s:" in the display). The issuer is CN=COMODO RSA Domain Validation Secure Server CA, but that intermediate certificate is missing (issuer is the "i:" part in the display).
To fix this, you need to fetch COMODO RSA Domain Validation Secure Server CA from [Intermediate #2 (SHA-2)] Comodo RSA Domain Validation Secure Server CA.
The intermediate certificate is already PEM encoded. Take your server certificate, and append the COMODO RSA Domain Validation Secure Server CA intermediate. That means there will be two certificates in the file. They will look like:
-----BEGIN CERTIFICATE-----
<server certificate>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<intermediate certificate>
-----END CERTIFICATE-----
Plug that into your site under the server certificate.
Unfortunately, its not enough to add just COMODO RSA Domain Validation Secure Server CA. You also need to add COMODO RSA Certification Authority. Its another missing intermediate certificate. You can get COMODO RSA Certification Authority from [Intermediate #1 (SHA-2)] COMODO RSA Certification Authority.
So they will look like:
-----BEGIN CERTIFICATE-----
<end entity/server certificate>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<intermediate certificate #2>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<intermediate certificate #1>
-----END CERTIFICATE-----
Users (relying parties) will still need to have/trust the issuer of the last missing intermediate (the last missing intermediate is COMODO RSA Domain Validation Secure Server CA). The issuer of the last missing intermediate is CN=AddTrust External CA Root, and it should be built-in to the browser or one of those cacerts.pem packs.
Once you concatenate the two missing intermediates certificates with the server certificate (and upload it), you can test as follows.
First, download the trust anchor. Its CN=AddTrust External CA Root, and it can be found at [KMCS] AddTrust External CA Root.
Second, run openssl s_client to verify. Notice the addition of the CAfile option and the "Verify result OK (0)".
$ openssl s_client -connect naturalvape.us:443 -CAfile addtrustexternalcaroot_kmod.crt
CONNECTED(00000003)
...
Start Time: 1407558078
Timeout : 300 (sec)
Verify return code: 0 (OK)
Your images are loading through http. Go to your wordpress settings, and change both the site urls so they start with 'https'.
http://userguide.expand2web.com/wp-content/forum-image-uploads/don/wordpress-site-url.jpg

OPENSSL connection to a public server gives X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY

I am writing a very basic SSL client to connect to a HTTPS web server. I can connect and process the request/response just fine. However OpenSSL is reporting UNABLE_TO_GET_ISSUER_CERT_LOCALLY, but so far I choose to ignore the error :-). Now I want to solve that part of the problem.
I am testing by connecting to a public SSL server on HTTPS, such as Google or Yahoo, and checking the return of SSL_get_verify_result(...).
As I understand it, I need the CA pem files for that specific site so that OpenSSL can verify the chain to a trusted certificate authority. In this case, that would be the authority that signed the certs for Google or Yahoo.
To get the PEM files which I expect should work, I opened my FireFox, navigated to those sites, and performed a View Certificate and exported each one up the list. So for example, I have a file called "GeoTrustGlobalCA.pem" which all looks good. In fact, when I went to the GeoTrust site directly and downloaded their root certificate, it is identical to the one I exported from FireFox, as I would expect.
So, for example with Google which showed two certificates in the tree in FireFox, I load each one with:
result = SSL_CTX_load_verify_locations(ctx,"GoogleInternetAuthorityG2.pem",NULL);
if (result == 0) {
puts("Opps... Can't load the certificate");
}
result = SSL_CTX_load_verify_locations(ctx,"GeoTrustGlobalCA.pem",NULL);
if (result == 0) {
puts("Opps... Can't load the certificate");
}
After that, the usual stuff to connect and communicate:
BIO_set_conn_hostname(bio, "www.google.com:https");
And get no errors when loading or connecting.
However, the verification does not work.
result = SSL_get_verify_result(ssl);
printf("The Verify Result is %d \n",result);
I get the return UNABLE_TO_GET_ISSUER_CERT_LOCALLY (error code 20).
So, am I missing some concept here? Wouldn't this give me the X509_V_OK result because it has the trusted certificates? There were only two that were up the chain from google.com, and I used them.
The second call to SSL_CTX_load_verify_locations is replacing the certificate from the first call.
You should combine your roots into a single file:
$ cat my-trusted-roots.pem
-----BEGIN CERTIFICATE-----
... (CA certificate in base64 encoding) ...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
... (CA certificate in base64 encoding) ...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
... (CA certificate in base64 encoding) ...
-----END CERTIFICATE-----
And then load that single file with SSL_CTX_load_verify_locations. See the OpenSSL docs on SSL_CTX_load_verify_locations. In partuclar, the NOTES section:
If CAfile is not NULL, it points to a file of CA certificates in PEM
format. The file can contain several CA certificates identified by
-----BEGIN CERTIFICATE-----
... (CA certificate in base64 encoding) ...
-----END CERTIFICATE-----
sequences. Before, between, and after the certificates text is allowed
which can be used e.g. for descriptions of the certificates.
Just bike shedding here...
result = SSL_get_verify_result(ssl);
printf("The Verify Result is %d \n",result);
That's one of three tests you need to perform.
The second test you need to perform is below. Anonymous Diffie-Hellman (ADH) does not use a certificate, so you need to check for that.
X509* cert = SSL_get_peer_certificate(ssl);
if(cert) X509_free(cert);
if(cert == NULL)
/* Error - Anonymous Diffie-Hellman */
SSL_get_peer_certificate bumps the reference count on the certificate, so you need a matching call to X509_free.
The third test you need to perform is hostname matching. OpenSSL 1.1.0 WILL perform hostname matching (and other name matching, like PKCS9 email addresses); but lesser versions, like 0.9.8 and 1.0.1, DO NOT perform the matching.
Thanks to this post I could finally get the SSL/TLS Client to work on Windows. I built openssl using MSYS2. I had to make some changes to the openssl-bio-fetch.tar.gz code so it could build/run in Windows/MSYS2, mostly adjusting the Makefile includes and setting -DNDEBUG to avoid the Posix signals.
However when running the code I got:
$ ./openssl-bio-fetch.exe
Warning: thread locking is not implemented
verify_callback (depth=1)(preverify=0)
Issuer (cn): DigiCert High Assurance EV Root CA
Subject (cn): DigiCert SHA2 Extended Validation Server CA
Error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
certificate verify failed
I had to download the 2 .pem files:
DigiCert High Assurance EV Root CA
DigiCert SHA2 Extended Validation Server CA
AND PASTE THE CERTIFICATES TO THE SAME .pem FILE ALREADY IN USE BY THE CODE: random-org-chain.pem
Thank You!

show entire certificate chain for a local certificate file

I have a certificate (for example this one) saved in a local file. Using openssl from the command line, how can I display the entire chain from this certificate to a root CA? I tried:
openssl verify -verbose -purpose sslserver -CApath /etc/ssl/certs InCommonServerCA.txt
and got this confusing output that only seems to show the leaf certificate:
InCommonServerCA.txt: C = US, O = Internet2, OU = InCommon, CN = InCommon Server CA
error 26 at 0 depth lookup:unsupported certificate purpose
OK
Any ideas?
For local certificates you can see the subject and direct issuer using:
openssl x509 -noout -subject -issuer -in test.crt
subject= /C=US/ST=Utah/L=SLC/O=My Organization/CN=my.server.com
issuer= /C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
But that doesn't indicate if the certificate includes any intermediate certificates or the full chain of trust. The verify command you listed will fail if your system cannot validate the chain (example: you are missing an intermediate certificate or the root is not trusted), showing an error message like:
error 20 at 0 depth lookup:unable to get local issuer certificate
If you want to verify each entry in the file, you can use this script to show the chain of trust for a local certificate:
~ % ssl_chain.sh google.crt
0: subject= /C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
issuer= /C=US/O=Google Inc/CN=Google Internet Authority G2
1: subject= /C=US/O=Google Inc/CN=Google Internet Authority G2
issuer= /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
2: subject= /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
issuer= /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
google.crt: OK
If you want to verify the chain and purpose, your openssl command is correct. The "OK" indicates the chain verifies. The error indicates there is an issue with that certificate being used for an sslserver purpose. It looks like your certificate is a CA cert, not a leaf cert.
What kind of chain info are you trying to display? You could look at the subject and issuer fields to show chaining. The verify command you used above proves that the one cert signed the other cert.

OpenSSL: unable to verify the first certificate for Experian URL

I am trying to verify an SSL connection to Experian in Ubuntu 10.10 with OpenSSL client.
openssl s_client -CApath /etc/ssl/certs/ -connect dm1.experian.com:443
The problem is that the connection closes with a Verify return code: 21 (unable to verify the first certificate).
I've checked the certificate list, and the Certificate used to sign Experian (VeriSign Class 3 Secure Server CA - G3) is included in the list.
/etc/ssl/certs/ca-certificates.crt
Yet I don't know why it is not able to verify the first certificate.
The entire response could be seen here:
https://gist.github.com/1248790
The first error message is telling you more about the problem:
verify error:num=20:unable to get local issuer certificate
The issuing certificate authority of the end entity server certificate is
VeriSign Class 3 Secure Server CA - G3
Look closely in your CA file - you will not find this certificate since it is an intermediary CA - what you found was a similar-named G3 Public Primary CA of VeriSign.
But why does the other connection succeed, but this one doesn't? The problem is a misconfiguration of the servers (see for yourself using the -debug option). The "good" server sends the entire certificate chain during the handshake, therefore providing you with the necessary intermediate certificates.
But the server that is failing sends you only the end entity certificate, and OpenSSL is not capable of downloading the missing intermediate certificate "on the fly" (which would be possible by interpreting the Authority Information Access extension). Therefore your attempt fails using s_client but it would succeed nevertheless if you browse to the same URL using e.g. FireFox (which does support the "certificate discovery" feature).
Your options to solve the problem are either fixing this on the server side by making the server send the entire chain, too, or by passing the missing intermediate certificate to OpenSSL as a client-side parameter.
Adding additional information to emboss's answer.
To put it simply, there is an incorrect cert in your certificate chain.
For example, your certificate authority will have most likely given you 3 files.
your_domain_name.crt
DigiCertCA.crt # (Or whatever the name of your certificate authority is)
TrustedRoot.crt
You most likely combined all of these files into one bundle.
-----BEGIN CERTIFICATE-----
(Your Primary SSL certificate: your_domain_name.crt)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(Your Intermediate certificate: DigiCertCA.crt)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(Your Root certificate: TrustedRoot.crt)
-----END CERTIFICATE-----
If you create the bundle, but use an old, or an incorrect version of your Intermediate Cert (DigiCertCA.crt in my example), you will get the exact symptoms you are describing.
SSL connections appear to work from browser
SSL connections fail from other clients
Curl fails with error: "curl: (60) SSL certificate : unable to get local issuer certificate"
openssl s_client -connect gives error "verify error:num=20:unable to get local issuer certificate"
Redownload all certs from your certificate authority and make a fresh bundle.
I came across the same issue installing my signed certificate on an Amazon Elastic Load Balancer instance.
All seemed find via a browser (Chrome) but accessing the site via my java client produced the exception javax.net.ssl.SSLPeerUnverifiedException
What I had not done was provide a "certificate chain" file when installing my certificate on my ELB instance (see https://serverfault.com/questions/419432/install-ssl-on-amazon-elastic-load-balancer-with-godaddy-wildcard-certificate)
We were only sent our signed public key from the signing authority so I had to create my own certificate chain file. Using my browser's certificate viewer panel I exported each certificate in the signing chain. (The order of the certificate chain in important, see https://forums.aws.amazon.com/message.jspa?messageID=222086)
Here is what you can do:-
Exim SSL certificates
By default, the /etc/exim.conf will use the cert/key files:
/etc/exim.cert
/etc/exim.key
so if you're wondering where to set your files, that's where.
They're controlled by the exim.conf's options:
tls_certificate = /etc/exim.cert
tls_privatekey = /etc/exim.key
Intermediate Certificates
If you have a CA Root certificate (ca bundle, chain, etc.) you'll add the contents of your CA into the exim.cert, after your actual certificate.
Probably a good idea to make sure you have a copy of everything elsewhere in case you make an error.
Dovecot and ProFtpd should also read it correctly, so dovecot no longer needs the ssl_ca option.
So for both cases, there is no need to make any changes to either the exim.conf or dovecot.conf(/etc/dovecot/conf/ssl.conf)
If you are using MacOS use:
sudo cp /usr/local/etc/openssl/cert.pem /etc/ssl/certs
after this Trust anchor not found error disappears
For those using zerossl.com certificates, drag and drop all certificates (as is) to their respective folders.
Cut and pasting text into existing files, may cause utf8 issues - depending upon OS, format and character spacings.