Openssl Certificate Chain - ssl

Executed the command:
openssl s_client -connect (redacted):443
and I get the output
depth=1 C = US, O = Let's Encrypt, CN = R3
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = *.(redacted)
verify return:1
---
Certificate chain
0 s:CN = *.(redacted)
i:C = US, O = Let's Encrypt, CN = R3
1 s:C = US, O = Let's Encrypt, CN = R3
i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
What does the error "unable to get local issuer certificate" mean? From the above I can see that the full chain is there, and that the root CA "DST Root CA X3" signed the "R3" cert. So surely that R3 cert does not need to be explicitly trusted? Is this good enough?
Is this cause for concern?

verify error:num=20:unable to get local issuer certificate
The trust chain from the leaf (server certificate) via the intermediate chain certificates (R3) must end in a locally trusted root CA (DST Root CA X3). Obviously this root CA is not locally trusted in the CA store used by your openssl setup (or maybe you have explicitly used -CApath or -CAfile).

Related

Openssl: error 20 at 0 depth lookup: unable to get local issuer certificate

I have 3 certificates rootca.pem, intermediateca.pem and server.pem
Openssl verify intermediateca by root is fine
openssl verify -verbose -CAfile rootca.pem intermediateca.pem
intermediateca.pem: OK
Server certificate, signed by intermediate - verification failed
openssl verify -verbose -CAfile rootca.pem -untrusted intermediateca.pem server.pem
CN = 2ip.ru
error 20 at 0 depth lookup: unable to get local issuer certificate
error server.pem: verification failed
I check hash subject-issuer of rootca intermediateca and intermediateca server. hash correct
I paste my certificate chain here
The Authority Key Identifier (AKI) is messed up in the certificates, which causes it to fail to build the trust path. Both the leaf certificate and the intermediate certificate have the AKI point to the root certificate:
# leaf
Issuer: C = RU, O = JSC Sberbank-AST, CN = int_ca
AKI: keyid:6C:C5:5B:22:4B:2D:CA:EC:C1:15:03:F6:5D:AD:C4:E8:4C:1D:06:89
# intermediate
Issuer: DC = ru, DC = sberbank-ast, CN = sberbank-ast-SUN-CA
AKI: keyid:6C:C5:5B:22:4B:2D:CA:EC:C1:15:03:F6:5D:AD:C4:E8:4C:1D:06:89
As can be seen, both leaf certificate and intermediate certificate wrongly claim to be issued by the same CA based on the Authority Key Identifier, while they correctly claim to be issued by different CA using the Issuer field.
But not only the Issuer field must match the Subject field of the issuer, the Authority Key Identifier must match the Subject Key Identifier of the issuer. While this is true for the relation between intermediate CA and root CA it is not true for the relation between leaf certificate and intermediate CA - and thus verify fails here.

build.shibboleth.net gives wrong certificate for some regions

One of our Jenkins builds is failing trying to connect to build.shibboleth.net. So I did some analysis.
Please look at these openssl results from 2 regions.
In South Asia region: It gives the cert of test.shibboleth.net, which is wrong.
$ openssl s_client -host build.shibboleth.net -port 443 -prexit -showcerts
CONNECTED(00000005)
depth=1 C = US, ST = Ohio, O = Shibboleth Consortium, CN = Shibboleth Project Intermediate CA
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/C=US/ST=Ohio/O=Shibboleth Consortium/CN=test.shibboleth.net
i:/C=US/ST=Ohio/O=Shibboleth Consortium/CN=Shibboleth Project Intermediate CA
...
...
In USA region: It gives the cert of shibboleth.net, which is correct.
$ openssl s_client -host build.shibboleth.net -port 443 -prexit -showcerts
CONNECTED(00000005)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = shibboleth.net
verify return:1
---
Certificate chain
0 s:CN = shibboleth.net
i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
...
...
And resolveip build.shibboleth.net gives 3.213.250.186 from both regions.
Why is it giving a wrong certificate for one region? I have no clue how to fix this. Any ideas?
I'd speculate that the results above aren't caused by region dependence but instead that you're using a different version of openssl on the two client machines.
We used to run the different vhosts on different IP addresses. Now, one address is used for everything and we rely on SNI to distinguish requests so that the right certificate is returned.
If the openssl client doesn't present the server name then you might get a default certificate instead of the one you expected. Whether openssl does that or not depends on the version of openssl in use (openssl version 1.1.1 does, earlier versions don't).
You can try adding -servername build.shibboleth.net to your openssl command to see if that changes the behaviour.

Can Root CA Certificate located in the middle of certificate path?

I made a program that connects to website(tls) and save certificate chain to files.
Sometimes certificate chain from a website is looking different from what I expected.
One of this certificate chain is issued from Sectigo(ex Comodo) CA.
I think "AddTrust External CA Root" should located in the last certificate of chain but is located in second certificate in its chain.(please look at below Certificate chain part )
$ openssl s_client -showcerts -connect adblockplus.org:443
CONNECTED(00000003)
depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=2 C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = Sectigo Limited, CN = Sectigo RSA Extended Validation Secure Server CA
verify return:1
depth=0 serialNumber = HRB 73508, jurisdictionC = DE, businessCategory = Private Organization, C = DE, postalCode = 50825, ST = Nordrhein-Westfalen, L = K\C3\B6ln, street = Lichtstra\C3\9Fe 25, O = Eyeo GmbH, OU = COMODO EV SSL, CN = www.adblockplus.org
verify return:1
---
Certificate chain
0 s:/serialNumber=HRB 73508/jurisdictionC=DE/businessCategory=Private Organization/C=DE/postalCode=50825/ST=Nordrhein-Westfalen/L=K\xC3\xB6ln/street=Lichtstra\xC3\x9Fe 25/O=Eyeo GmbH/OU=COMODO EV SSL/CN=www.adblockplus.org
i:/C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Extended Validation Secure Server CA
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
1 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
2 s:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
3 s:/C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Extended Validation Secure Server CA
i:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
---
Server certificate
subject=/serialNumber=HRB 73508/jurisdictionC=DE/businessCategory=Private Organization/C=DE/postalCode=50825/ST=Nordrhein-Westfalen/L=K\xC3\xB6ln/street=Lichtstra\xC3\x9Fe 25/O=Eyeo GmbH/OU=COMODO EV SSL/CN=www.adblockplus.org
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Extended Validation Secure Server CA
my question is:
Is this case is normal?
Is the web server(adblockplus this time) making Certificate path ?
How to determine valid certificate path?
Any comments are welcome. thanks
A TLS-was-SSL server is supposed to send the certificate chain in the correct order in the handshake, but some don't, and most clients including OpenSSL will still handle it correctly, by matching up issuer=subject names, as long as the leaf (end-entity) cert is first. Note the trace of the validation process:
depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=2 C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = Sectigo Limited, CN = Sectigo RSA Extended Validation Secure Server CA
verify return:1
depth=0 serialNumber = HRB 73508, jurisdictionC = DE, businessCategory = Private Organization, C = DE, postalCode = 50825, ST = Nordrhein-Westfalen, L = K\C3\B6ln, street = Lichtstra\C3\9Fe 25, O = Eyeo GmbH, OU = COMODO EV SSL, CN = www.adblockplus.org
verify return:1
You can see that the certificates were used in the correct top-to-bottom order even though they weren't received in the correct bottom-to-top order.
This misbehavior is common enough TLS 1.3 was changed to officially permit it. Compare TLS 1.2 in RFC 5246 7.4.2:
certificate_list ... The sender's
certificate MUST come first in the list. Each following
certificate MUST directly certify the one preceding it. Because
certificate validation requires that root keys be distributed
independently, the self-signed certificate that specifies the root
certificate authority MAY be omitted from the chain, under the
assumption that the remote end must already possess it in order to
validate it in any case.
to TLS 1.3 in RFC 8446 4.4.2, emphasis added:
... The sender's certificate MUST come in the first
CertificateEntry in the list. Each following certificate SHOULD
directly certify the one immediately preceding it. Because
certificate validation requires that trust anchors be distributed
independently, a certificate that specifies a trust anchor MAY be
omitted from the chain, provided that supported peers are known to
possess any omitted certificates.
Note: Prior to TLS 1.3, "certificate_list" ordering required each
certificate to certify the one immediately preceding it; however,
some implementations allowed some flexibility. Servers sometimes
send both a current and deprecated intermediate for transitional
purposes, and others are simply configured incorrectly, but these
cases can nonetheless be validated properly. For maximum
compatibility, all implementations SHOULD be prepared to handle
potentially extraneous certificates and arbitrary orderings from any
TLS version, with the exception of the end-entity certificate which
MUST be first.
(And the same is true in the other direction for client certificates, but they are used only very rarely, while server certificates almost always are.)

How to verify certificate chain with openssl

I am trying to verify a certificate file with OpenSSL. Can you explain me why s_client connection succeeds, but verify file with the same certificate chain fails? How can I verify the file?
Note I compiled OpenSSL 1.0.1k myself, it shouldn't be using any distro-specific config. And I provided the same CAfile to both commands.
$ openssl s_client -CAfile /etc/pki/tls/certs/ca-bundle.crt -connect www.google.com:443
WARNING: can't open config file: /usr/local/ssl/openssl.cnf
CONNECTED(00000003)
depth=3 C = US, O = Equifax, OU = Equifax Secure Certificate Authority
verify return:1
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 C = US, O = Google Inc, CN = Google Internet Authority G2
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google Inc, CN = www.google.com
verify return:1
---
Certificate chain
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.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
---
...
Verify return code: 0 (ok)
---
If I run it with -showcerts argument, it outputs all three certificates sent from server. I concatenated them into file google.pem. But the chain can't be verified. See:
$ openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt google.pem
WARNING: can't open config file: /usr/local/ssl/openssl.cnf
google.pem: C = US, ST = California, L = Mountain View, O = Google Inc, CN = www.google.com
error 20 at 0 depth lookup:unable to get local issuer certificate
Applying a patch suggested on https://stackoverflow.com/a/27606964/1823988 doesn't help.
I found it. openssl verify doesn't expect certificate file to contain its chain. Chain needs to be passed with -untrusted argument. It works with the same file, trust is still determined by finding a trusted root in -CAfile.
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt -untrusted google.pem google.pem

SSL Certificate - Certification Path in browser different from Certificate Chain File

I recently purchased a free SSL certificate from Comodo. It came with a certification authority bundle file that contains all of the intermediate certificates as well as the root certificate. When I run the command "openssl s_client -connect www.mydomain.com:443 -showcerts" it shows a certificate path that looks like this:
depth=4 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=3 C = US, ST = UT, L = Salt Lake City, O = The USERTRUST Network, OU = http://www.usertrust.com, CN = UTN-USERFirst-Hardware
verify return:1
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = EssentialSSL CA
verify return:1
depth=0 OU = Domain Control Validated, OU = Free SSL, CN = www.mydomain.com
verify return:1
However, when I go to www.mydomain.com in any browser and look at the certificate presented by the server, it shows the following certificate path (taken from IE9 certificate window):
COMODO
EssentialSSL CA
www.mydomain.com
Notice that there are fewer certificates in the chain (depth of 2 versus 4 from the openssl command), and that the root certificate is the COMODO certificate as opposed to the AddTrust External CA Root certificate. Can someone explain why the browser shows a different path than the openssl command?
Note, in both cases the certificate chain presented by the server passes validation (verify result 0 from openssl, no warnings in the browser).
IE9 has the comodo ca as a trusted authority in its trust chain, and therefore doesn't show the signers of the comodo ca.
OpenSSL s_client -showcerts shows the whole certificate chain.