How to obtain a certificate with sole root CA of ISRG Root X1?

What I'm trying to do:

  • I want set up a server that behaves the same as (which returns a cert that only chains up to ISRG Root X1) so that I can gain confidence that our existing android clients running an old android OS can hit an endpoint with certs issued stating in January 2020. (see this post from letsencrypt for details) The reason I want to have my own server up is that there are certain endpoints I'd like to hit and have it return 201 so subsequent calls can be made against other endpoints. Without hosting my own server, I cannot have test certain scenarios aside from "user can log in".

My test domain is:

OS: ManjaroLinux 20.1.2
I installed certbot via pacman and the version is certbot 1.9.0. My openssl version is: OpenSSL 1.1.1h 22 Sep 2020

I ran this command:

sudo certbot certonly --manual --preferred-chain "ISRG Root X1" --preferred-challenges dns

I obtained a cert via this command:

sudo certbot certonly --manual --preferred-chain "ISRG Root X1" --preferred-challenges dns

Then I configured my DNS records so that I have an A record and a TXT record, and the challenge was completed successfully. Files of fullchain.pem / chain.pem / cert.pem / privatekey.pem were generated.

Then I went to identrust and and downloaded the .pem files for:

  • (root) ISRG Root X1
  • (root) DST Root CA X3
  • (Intermediate) Let’s Encrypt R3

But when I run the following:

sudo openssl verify -CAfile /etc/letsencrypt/live/<my_domain>/cert.pem lets-encrypt-r3.pem identrust.pem isrgrootx1.pem lets-encrypt-r3-cross-signed.pem

It says that all of it is verified.

lets-encrypt-r3.pem: OK
identrust.pem: OK
isrgrootx1.pem: OK
lets-encrypt-r3-cross-signed.pem: OK

When I load a website that serves that exact same cert, I see that the chain is:
<my domain> -> R3 -> DST ROOT X3

Behavior differed from what I expected because:

I expected the output of openssl verify against identrust.pem to say ERROR and lets-encrypt-r3-cross-signed.pem to also say ERROR and only OK for lets-encrypt-r3 and isrgrootx1.pem

and I expect that on firefox, the certificate chain when I go to my domain to say: <my domain> -> R3 -> ISRG Root X1

Am I configuring something incorrectly? Why does openssl verify return OK for all? Or is it impossible to get a cert that only points to ISRG Root X1 at this moment?

1 Like

Does the second certificate in fullchain.pem match lets-encrypt-r3.pem? If so, you're all set.

I'm not sure what you're trying to accomplish with your openssl verify command.

Your browser may be caching a prior authorization chain if it's ever seen that domain with a DST Root X3 signed intermediate before. Looking at the chain in your browser is not a reliable method to determine what the server is currently sending.

1 Like

That looks good to me.

Perhaps LE forgot to add the ISRG Root X1 signed alternative chain when they changed the intermediate signing key from X3 to R3? @lestaff any thoughts?

Also, you van manually change the intermediate provided (erroneously) to the one you want in the mean time. Just replace chain.pem (in /archive/ as the files in /live/ are just symlinks) to the one you want and als update fullchain to contain the correct intermediate (also in /archive/).

By the way, currently, your server isn't serving any intermediate certificate at the moment:

$ openssl s_client -connect
depth=0 CN =
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN =
verify error:num=21:unable to verify the first certificate
verify return:1
Certificate chain
 0 s:CN =
   i:C = US, O = Let's Encrypt, CN = R3

Kinda strange my Android 10 chooses the DST root to chain up to, perhaps it didn't cache the intermediate signed by ISRG Root X1 yet..


I think the command you're looking for is (UPDATED)

openssl verify -verbose -CAfile isrgrootx1.pem -untrusted lets-encrypt-r3.pem fullchain.pem

That is, you want to make sure that your fullchain verifies up to the CA given in the isrgrootx1 file, with the intermediate allowed for building the chain but not explicitly trusted.

(And as others are saying, you need to ensure that you're serving the fullchain.pem from your server, where the second certificate in that file should match lets-encrypt-r3.pem exactly.)


Remember that there is no such thing as "a cert that only points to ISRG Root X1". When you configure your server to present the chain Your Cert <-- R3 <-- ISRG Root X1, that acts only as a hint to browsers. Browsers are free to ignore that hint, and instead build the chain Your Cert <-- R3 <-- DST Root X3 during validation.

To double-check that your server is providing the chain you want, you can use a tool like certigo:

$ ~/go/bin/certigo connect --verbose
** TLS Connection **
Version: TLS 1.2
Cipher Suite: ECDHE_RSA key exchange, AES_256_GCM_SHA384 cipher

Serial: 331862780954954513440435273324005883452576
Valid: 2020-12-03 04:09 UTC to 2021-03-03 04:09 UTC
Signature: SHA256-RSA
Subject Info:
Issuer Info:
	Country: US
	Organization: Let's Encrypt
	CommonName: R3
Subject Key ID: 49:CC:A8:EE:8A:02:54:39:36:FC:49:DD:E6:C1:59:E8:72:09:7E:9E
Authority Key ID: 14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D:8B:14:C2:C6
Basic Constraints: CA:false
OCSP Server(s):
Issuing Certificate URL(s):
Key Usage:
	Digital Signature
	Key Encipherment
Extended Key Usage:
	Server Auth
	Client Auth
DNS Names:

Failed to verify certificate chain:
	x509: certificate signed by unknown authority

As @Osiris pointed out, it looks like you're currently not serving a chain at all; you're only serving your end-entity certificate and not providing an intermediate. You shouldn't need to download any of our certs from, instead simply use the fullchain.pem file which was supplied by your certbot invocation.


Thanks for the help everyone. This ended up being a config issue on the server side. I've since reconfigured our server to return what's in fullchain.pem (instead of cert.prem) and now I'm able to verify the chain via openssl s_client -connect


This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.