“Unable to get Local Issuer Certificate” is a common SSL certificate error. It is related to the incomplete certificate chain such as (most commonly) missing the intermediate certificate. The fix is to ensure the entire certificate chain is present.
We will dive into this issue to see why this happens and how to fix it.
Understanding certificate chain
A certificate chain is an ordered list of certificates, containing an SSL/TLS server certificate, intermediate certificate, and Certificate Authority (CA) Certificates, that enable the receiver to verify that the sender and all CA’s are trustworthy.
- Root Certificate. A root certificate is a digital certificate that belongs to the issuing Certificate Authority. It comes pre-downloaded in most browsers and is stored in what is called a “trust store.” The root certificates are closely guarded by CAs.
- Intermediate Certificate. Intermediate certificates branch off root certificates like branches of trees. They act as middle-men between the protected root certificates and the server certificates issued out to the public. There will always be at least one intermediate certificate in a chain, but there can be more than one.
- Server Certificate. The server certificate is the one issued to the specific domain the user is needing coverage for.
We will use these files in this example.
- CA certificate file (usually called ca.pem or cacerts.pem)
- Intermediate certificate file (if exists, can be more than one. If you don’t know if you need an intermediate certificate, run through the steps and find out)
- Server certificate file
How to get a free SSL certificate?
If you need a free SSL certificate for your website, Elementor Cloud Website is a great option. They offer fast speeds, good uptime, and excellent customer support. It is an end-to-end solution gives you everything you need in one place for your website. Web Hosting on Google Cloud + SSL certificate + WordPress + Website Builder + Templates.
We recommend using Elementor Cloud Website to build a website. It is very easy to start. You can get your website online in minutes. The price is $99 for one year. Plus, they offer a 30-day money-back guarantee, so you can try it out with no risk.
How do Certificate Chains work?
When we install our TLS certificate, we also be sent an intermediate root certificate or bundle.
When a browser downloads our website’s TLS certificate upon arriving at our homepage, it begins chaining that certificate back to its root. It will begin by following the chain to the intermediate that has been installed, from there it continues tracing backwards until it arrives at a trusted root certificate.
If the certificate is valid and can be chained back to a trusted root, it will be trusted. If it can’t be chained back to a trusted root, the browser will issue a warning about the certificate.
View Certificate Chain
Use the openssl utility that can display a certificate chain. The following command will display the certificate chain for google.com.
openssl s_client -connect google.com:443 -servername google.com
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=*.google.com
i:/C=US/O=Google Inc/CN=Google Internet Authority G2
1 s:/C=US/O=Google Inc/CN=Google Internet Authority G2
i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
2 s:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
In the openssl output, the numbered lines start with the server certificate (#0) followed by the intermediate (#1) and the root (#2).
The s: indicates the certificate subject, and i: indicates the issuing certificate’s subject.
Guidelines to verify the certificate chain is valid
- Subject of each certificate matches the Issuer of the preceding certificate in the chain (except for the Entity certificate).
- Subject and Issuer are the same for the root certificate.
If the certificates in the chain adhere to these guidelines, then the certificate chain is considered to be complete and valid.
- The Subject of the intermediate certificate matches the Issuer of the entity certificate.
- The Subject of the root certificate matches the Issuer of the intermediate certificate.
- The Subject and Issuer are the same in the root certificate.
Example of a valid certificate chain
server certificate
openssl x509 -text -in entity.pem | grep -E '(Subject|Issuer):'
Issuer: C = US, O = Google Trust Services, CN = GTS CA 1O1
Subject: C = US, ST = California, L = Mountain View, O = Google LLC, CN = *.enterprise.apigee.com
Intermediate certificate
openssl x509 -text -in intermediate.pem | grep -E '(Subject|Issuer):'
Issuer: OU = GlobalSign Root CA – R2, O = GlobalSign, CN = GlobalSign
Subject: C = US, O = Google Trust Services, CN = GTS CA 1O1
Root certificate
openssl x509 -text -in root.pem | grep -E '(Subject|Issuer):'
Issuer: OU = GlobalSign Root CA – R2, O = GlobalSign, CN = GlobalSign
Subject: OU = GlobalSign Root CA – R2, O = GlobalSign, CN = GlobalSign
Check SSL Certificate with OpenSSL
Validate certificate chain with server and root Certificate
openssl verify cert.pem
cert.pem: C = Country, ST = State, O = Organization, CN = FQDN
error 20 at 0 depth lookup:unable to get local issuer certificate
We can use the following two commands to make sure that the issuer in the server certificate matches the subject in the ca certificate.
openssl x509 -noout -issuer -in cert.pem
issuer= /CN=the name of the CA
$ openssl x509 -noout -subject -in ca.pem
subject= /CN=the name of the CA
In the following case, we need to add the CAfile to verify the root certificate.
$ openssl verify -CAfile ca.pem cert.pem
cert.pem: OK
Validate certificate chain with server, intermediate, and root Certificate
$ openssl verify cert.pem
cert.pem: C = Countrycode, ST = State, O = Organization, CN = yourdomain.com
error 20 at 0 depth lookup:unable to get local issuer certificate
To complete the validation of the chain, we need to provide the CA certificate file and the intermediate certificate file when validating the server certificate file.
We can do that using the parameters CAfile (to provide the CA certificate) and untrusted (to provide intermediate certificate):
$ openssl verify -CAfile ca.pem -untrusted intermediate.cert.pem cert.pem
cert.pem: OK
If we have multiple intermediate CA certficates, we can use the untrusted parameter multiple times like -untrusted intermediate1.pem -untrusted intermediate2.pem .
Fix routines:X509_check_private_key:key values mismatch in 2 Ways
Related:
- Exploring SSL Certificate Chain with Examples
- Understanding X509 Certificate with Openssl Command
- OpenSSL Command to Generate View Check Certificate
- Converting CER CRT DER PEM PFX Certificate with Openssl
- SSL vs TLS and how to check TLS version in Linux
- Understanding SSH Key RSA DSA ECDSA ED25519
- Understanding server certificates with Examples
This error might be occurred when executing such as openssl, wget or curl.
Contents
Error Log
Here is the error log.
Terminal
# openssl s_client -connect noknow.info:443 CONNECTED(00000003) depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 verify error:num=20:unable to get local issuer certificate verify return:1 depth=0 CN = noknow.info verify return:1 --- Certificate chain 0 s:CN = noknow.info i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 i:O = Digital Signature Trust Co., CN = DST Root CA X3 --- Server certificate -----BEGIN CERTIFICATE----- MIIFTjCCBDagAwIBAgISA7nL0QichqmVlJkULrbQaQF9MA0GCSqGSIb3DQEBCwUA ... -----END CERTIFICATE----- subject=CN = noknow.info issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 --- No client certificate CA names sent Peer signing digest: SHA512 Peer signature type: RSA Server Temp Key: ECDH, P-256, 256 bits --- SSL handshake has read 3225 bytes and written 439 bytes Verification error: unable to get local issuer certificate --- New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES256-GCM-SHA384 Session-ID: C6D0CDE9976936913608879... Session-ID-ctx: Master-Key: D9CC0E2CBBF620461B1D... PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 300 (seconds) TLS session ticket: 0000 - fe a3 38 9f f0 f9 ef 37-27 07 c3 38 9b 87 e2 f6 ..8....7'..8.... 0010 - b4 e1 3b d1 29 f8 42 bb-a7 76 7e 0b 87 2b e3 00 ..;.).B..v~..+.. 0020 - 66 f9 fc 5a 3f c1 d7 92-a8 89 b5 5d 30 68 cc d3 f..Z?......]0h.. ... Start Time: 1565257700 Timeout : 7200 (sec) Verify return code: 20 (unable to get local issuer certificate) Extended master secret: no ---
Check the OpenSSL Related Path
Check the path which is for OpenSSL.
Terminal
# openssl version -d
OPENSSLDIR: "/usr/local/openssl"
Solution Way1
Create the symbolic link for the existing certificate under path OPENSSLDIR.
The name of the link should be cert.pem.
Terminal
// For Ubuntu # find / -name ca-certificates.crt /etc/ssl/certs/ca-certificates.crt # ln -s /etc/ssl/certs/ca-certificates.crt /usr/local/openssl/cert.pem
Solution Way2
Download the extracted CA certificates from Mozilla under the path OPENSSLDIR.
The name of the link should be cert.pem.
Terminal
// For Ubuntu # wget https://curl.haxx.se/ca/cacert.pem --no-check-certificate # ln -s /usr/local/openssl/cacert.pem /usr/local/openssl/cert.pem
We have a Debian 7 X64 server running Apache Tomcat on which I am trying to install a certificate from LetsEncrypt. I am partially successful as I am getting an error where certificate verification is failing.
I followed the guide on letsencrypts community site https://community.letsencrypt.org/t/how-to-use-the-certificate-for-tomcat/3677
For adding LetsEncrypt to Tomcat, I had to create a new JKS and add the converted keys into the JKS keystore, which is referred in Apache Tomcat. This are the steps I followed:
openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -CAfile chain.pem -caname root -out fullchain_and_key.p12 -name tomcat
keytool -importkeystore -deststorepass PASS -destkeypass PASS -destkeystore MyDSKeyStore.jks -srckeystore fullchain_and_key.p12 -srcstoretype pkcs12 -srcstorepass PASS -alias tomcat
keytool -import -trustcacerts -alias root -file chain.pem -keystore MyDSKeyStore.jks
openssl s_client -connect mydomain.de:443
CONNECTED(00000003)
depth=1 CN = Fake LE Intermediate X1
verify error:num=20:unable to get local issuer certificate
---
Certificate chain
0 s:/CN=mydomain.de/serialNumber=fa4eff65933b17aa84130eaabf96ce401ae5
i:/CN=Fake LE Intermediate X1
1 s:/CN=Fake LE Intermediate X1
i:/CN=Fake LE Root X1
---
Server certificate
-----BEGIN CERTIFICATE-----
RANDOMDATA ending with ==
-----END CERTIFICATE-----
subject=/CN=mydomain.de/serialNumber=fa4eff65933b17aa84130eaabf96ce401ae5
issuer=/CN=Fake LE Intermediate X1
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3002 bytes and written 441 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES128-GCM-SHA256
Session-ID: 5729C1080CFF1B8662C6F3C007C783E4066D985A23D99BAD85C5A721ACF6C866
Session-ID-ctx:
Master-Key: 3972DBC0072395E1F4C5BE25A23A165DA4AB301DAEDF2753F1FD6FFDBD35BDF8C42B7C6520D3785D425B5D37FE3CD603
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1462354184
Timeout : 300 (sec)
Verify return code: 20 (unable to get local issuer certificate)
---
read:errno=0
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true" maxThreads="200"
scheme="https" secure="true" clientAuth="false" sslProtocol="TLS"
keystoreFile="/etc/letsencrypt/live/mydomain.de/MyDSKeyStore.jks"
keystorePass="PASS" keyPass="PASS" keyAlias="tomcat"
/>
This has kept me busy for a good number of hours. I have read a good deal other articles and Stackexchange-questions, and tried other things, but no positive result so far.
Running Ubuntu20/Nginx/Openssl v1.1.1.
Using wget, openssl s_client or curl on normal web resources, I get the message: «Verify return code: 20 (unable to get local issuer certificate)», or equivalent.
$ openssl s_client -connect google.com:443
CONNECTED(00000003)
depth=2 C = US, O = Google Trust Services LLC, CN = GTS Root R1
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=1 C = US, O = Google Trust Services LLC, CN = GTS CA 1C3
verify return:1
depth=0 CN = *.google.com
verify return:1
---
Certificate chain
0 s:CN = *.google.com
i:C = US, O = Google Trust Services LLC, CN = GTS CA 1C3
1 s:C = US, O = Google Trust Services LLC, CN = GTS CA 1C3
i:C = US, O = Google Trust Services LLC, CN = GTS Root R1
2 s:C = US, O = Google Trust Services LLC, CN = GTS Root R1
i:C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIN...
A bit of background. The SSL-handshake used to work for these common web resources. But I had an application that required a self-signed certificate to be added to the trusted CA-certificate store. Worked on that for a good twenty hours, tried many things. In the end decided to ‘start anew’ and delete my whole trusted certificate store, by deleting everything in /etc/ssl/certs/ and /usr/(local/)share/ca-certificates/) and restoring backups of common CA-certs in these folders, and a restore backup of /etc/ca-certificates.conf. Then ran update-ca-certificates. Also: I downgraded OpenSSL from v1.1.1 to 1.0.2, and then upgraded it again from 1.0.2 to 1.1.1.
Output below to demonstrate that it looks alright.
$ update-ca-certificates -f
Clearing symlinks in /etc/ssl/certs...
done.
Updating certificates in /etc/ssl/certs...
129 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
As far as I see, my trusted cert-store seems fine: it contains the requested root-certificates in the chain. Notice in the above example there are two root-certificates: (1) C = US, O = Google Trust Services LLC, CN = GTS Root R1, and (2) C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA.
I am sure these two root-certificates are in my trusted CA-store. Here’s a snippet of the output from a trick suggested by Marlon in NginX client cert authentication fails with “unable to get issuer certificate”
$ awk -v cmd='openssl x509 -noout -subject' ' /BEGIN/{close(cmd)};{print | cmd}' < /etc/ssl/certs/ca-certificates.crt
...
subject=OU = GlobalSign ECC Root CA - R4, O = GlobalSign, CN = GlobalSign
subject=OU = GlobalSign ECC Root CA - R5, O = GlobalSign, CN = GlobalSign
subject=C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
subject=OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
subject=OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
subject=OU = GlobalSign Root CA - R6, O = GlobalSign, CN = GlobalSign
...
subject=C = US, O = Google Trust Services LLC, CN = GTS Root R1
subject=C = US, O = Google Trust Services LLC, CN = GTS Root R2
subject=C = US, O = Google Trust Services LLC, CN = GTS Root R3
subject=C = US, O = Google Trust Services LLC, CN = GTS Root R4
So the root-certificates that the host in my example (google.com) uses are there in my trusted CA-store. Why am I still getting «Verification error: unable to get local issuer certificate»?
Additionally, I’ll add the output when I explicitly define the path to the trusted CA-cert store. The SSL-handshake succeeds! What am I overlooking?
$ openssl s_client -CApath /etc/ssl/certs -connect google.com:443
CONNECTED(00000003)
depth=2 C = US, O = Google Trust Services LLC, CN = GTS Root R1
verify return:1
depth=1 C = US, O = Google Trust Services LLC, CN = GTS CA 1C3
verify return:1
depth=0 CN = *.google.com
verify return:1
---
Certificate chain
0 s:CN = *.google.com
i:C = US, O = Google Trust Services LLC, CN = GTS CA 1C3
1 s:C = US, O = Google Trust Services LLC, CN = GTS CA 1C3
i:C = US, O = Google Trust Services LLC, CN = GTS Root R1
2 s:C = US, O = Google Trust Services LLC, CN = GTS Root R1
i:C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIN...
...
-----END CERTIFICATE-----
subject=CN = *.google.com
issuer=C = US, O = Google Trust Services LLC, CN = GTS CA 1C3
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 6523 bytes and written 392 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
To conclude: I am likely overlooking something, some setting or parameter that may have gotten reset, or set wrongly during my hours of tinkering with the system. However, I just cannot see it, and the sources I’ve read and tried so far mostly mention making sure my trusted CA-cert store is complete, which I think it is. What am I overlooking? Where should I look, or what should I do to get a grip on this problem?