0

I'm trying to understand openssl and some cert issues I was trying to track down. These certs were issued from Let's Encrypt. I will use their site as an example because I see the same behavior there. First, I run openssl (OpenSSL 3.0.1 14 Dec 2021)

openssl s_client -showcerts -connect letsencrypt.org:443 -servername letsencrypt.org

and I get

depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = lencr.org
verify return:1

along with the certs.

For Internet Security Research Group, CN = ISRG Root X1 I see this

2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
   a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
   v:NotBefore: Jan 20 19:14:03 2021 GMT; NotAfter: Sep 30 18:14:03 2024 GMT
-----BEGIN CERTIFICATE-----
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
...

This is Let's Encrypt's Cross-signed by DST Root CA X3 cert rather than the Self-Signed ISRG Root cert.

I have some old java code running on 1.8.0_131-b11 that makes API calls. The calls stopped working and I attempted to use openssl to retrieve the root cert so I could add that to my java keystore. The cert returned from openssl didn't fix the issue and I eventually added the Self-Signed ISRG Root and everything started working.

I also tried adding the -trusted_first option to my openssl command but that displays the same certs.

Why is openssl showing Let's Encrypt's Cross-Signed cert instead of their Self-Signed cert? How do I get the correct cert chain using openssl?

  • openssl simply shows the cert chain that the server serves. Modern browsers will sometimes build a different cert chain, that chains-up to a different root certificate in the browser's trust store. See https://security.stackexchange.com/questions/258190/what-could-cause-classic-err-cert-date-invalid-when-i-can-confirm-no-error-fro/ for more info. – mti2935 Feb 01 '22 at 20:18
  • Java 8u131 released in 2017-03 does not have the ISRG root in cacerts unless you manually add it, although 8u144 in 2017-07 does; that was basically 2 years after LE/ISRG started operating. (Presumably 8u141 also did, but that was withdrawn before I could download it.) _If_ the ISRG root is present, Java will successfully validate to it, ignoring the cross cert to the expired DST X3 root. We had _lots_ of Qs about the LE/DST expiry when it occurred in early Oct on this and other Stacks; searching a little should find plenty. – dave_thompson_085 Feb 01 '22 at 23:36
  • Thanks for the answers but I'm still wondering how/if I can get the correct cert chain using openssl. I'm missing something here. – AnaphylacticInternet Feb 02 '22 at 16:20

1 Answers1

2

I don't think you can get a different chain from the server. I think the server will send the same chain to all clients that support ECDSA (and another chain to clients that don't support ECDSA and need an RSA-only chain).

So you're sent the cross signed cert and not the self signed cert.

If your client doesn't trust the ISRG root but trusts "DST Root CA X3" (and doesn't mind that it's expired), it thinks the chain contains only the leaf and intermediates (no root) and it links to a trusted root the server didn't send, and trusts the chain.

If your client doesn't trust the DST root anymore, but trusts the self-signed ISRG root, the client thinks the sent chain also contained the root, which was not needed but is allowed, and trusts the chain. It can recognize that the subject key identifier and the public key in the cross-signed and the self-signed certs are the same, so it's a match.

If your client trusts neither the expired DST root nor the ISRG root, it doesn't trust the chain.

Z.T.
  • 7,768
  • 1
  • 20
  • 35