Expo CLI - Error: certificate has expired

Hi,

I’m trying to start my expo local server, but get the following message in console:

An Expo user account is required to proceed.
How would you like to authenticate? Log in with an existing Expo account
Fetching the user profile failed
certificate has expired

It then prompts me to login with my Expo Account, but then gives me the following error:

Error: certificate has expired
    at TLSSocket.<anonymous> (_tls_wrap.js:1105:38)
    at emitNone (events.js:106:13)
    at TLSSocket.emit (events.js:208:7)
    at TLSSocket._finishInit (_tls_wrap.js:639:8)
    at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:469:38)

This is a legacy ExpoKit (SDK 36) project that I am doing maintenance on, so the expo-cli version is 3.11.0. The reason this version is used is because of the following warning message: Your project is using deprecated "rnpm" config that will stop working from next release. Please use a "react-native.config.js" file to configure the React Native CLI. Migration guide: https://github.com/react-native-community/cli/blob/master/docs/configuration.md.

I have tried researching this and it appears it is on Expo’s end that something has changed. Can anyone help?

Thanks.

Hey @bertied, unfortunately we are unable to provide support for projects that fall outside of our support cycle. Is there a particular reason the project is being kept on SDK36/using ExpoKit at this time?

Cheers,
Adam

@bertied Do you get this if you just run expo login with an up-to-date version of expo-cli?

Hmm, I am getting the same issue as you are getting when I try to send a push notification using the API

{ FetchError: request to https://exp.host/--/api/v2/push/send failed, reason: certificate has expired

I’ve already ran expo credentials:manager to update the credentials, but the problem persist, anyone has any idea how to fix?

@chiechelski

This is not about the credentials for signing an app but rather about the SSL certificate chain on exp.host and how your machine processes it.

I think that you do not have an up-to-date ISRG Root X1 CA certificate in your trusted root store. You might also need to upgrade the version of OpenSSL on your system (which I suppose would probably include recompiling/upgrading node or whatever you’re using to send push notifications.)
EDIT: Probably also a good idea to remove the expired “DST Root CA X3” certificate from your trust store as mentioned in the blog post from OpenSSL.org.

See this thread for a bunch of details:

Also these:

Here’s one from openssl.org:
https://www.openssl.org/blog/blog/2021/09/13/LetsEncryptRootCertExpire/

Possible cause

The most likely cause of this issue is that you’re using an older version of OpenSSL and an older set of root CA certificates, both of which are the case with no-longer-supported versions of Node.js.

(Today, Node 12.13.0 and up are supported by the Node.js team, which approximately matches Expo’s policy for supported versions of Node. In April 2022, Node 14.15.0 will be the oldest version supported by the Node.js team.)

Technical details

Let’s Encrypt, the service we use for many of our TLS certificates, recently changed their root certificate from “DST Root CA X3”, which is now expired, to “ISRG Root X1”. For compatibility with older versions of Android (which a service like Expo especially needs!), the new ISRG Root X1 was cross-signed by DST Root CA X3. This causes issues with OpenSSL 1.0.x and is fixed in OpenSSL 1.1.0 and newer.

You can check the version of OpenSSL you’re using in Node.js with:

node -e 'console.log(process.versions.openssl)'

1.1.1l+quic

If you see an version number older than OpenSSL 1.1.0, this likely explains the issue.

Solution

The best solution in this case is to upgrade your version of Node. While upgrading to Node 16, the current version, is probably a good idea, you also can upgrade to Node 12. The latest version of Node 12.22.6 contains OpenSSL 1.1.1l and the newer root CA certificates.

Dangerous solution

The NODE_TLS_REJECT_UNAUTHORIZED environment variable disables all TLS verification, allowing any certificate, including malicious ones, to work. If you understand your threat model well, this footgun could be useful. However, upgrading Node is obviously more secure.

1 Like

We have a lot of devices with electron installed that have stopped sending notifications. I can ask all my customers to update this devices but there is another way.

The certificate chain looks like this:

0 s:CN = api.expo.dev
i:C = US, O = Let’s Encrypt, CN = R3
1 s:C = US, O = Let’s Encrypt, CN = R3
i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1
i:O = Digital Signature Trust Co., CN = DST Root CA X3

As you can see, you are still propagating the old DST Root CA X3. Some versions of node use the old X3 by default, but if it is missing they work perfectly with the ISRG Root X1.

As you can see here:

As Letsencrypt suggest, you could generate the certificates with the option: --preferred-chain=“ISRG Root X1”. This option can also be added in the renewal .conf file:

preferred_chain = ISRG Root X1

This way the certificate chain will change. You will stop propagating expired cert and the compatibility with older versions will be kept.

Hi @mael

There is a conflict between old versions of OpenSSL and old versions of Android. The server can either stay as it is now, which will allow old versions of Android to continue working, or it can make the change you suggest and allow old versions of OpenSSL to work. There’s no way to get both old versions of OpenSSL and old versions of Android to work at the same time.

It seems that the Expo team has decided that it’s more important to support old versions of Android than old versions of OpenSSL. However, it could be that they had not considered your use case.

We’re using Let’s Encrypt’s recommended configuration, which they designed to keep Android apps working and is something we need to do. The DST Root CA X3 certificate is intentionally used to cross-sign the ISRG Root X1 certificate.

In this post, the Let’s Encrypt team explains the two certificate chains. There’s the longer one that is cross-signed with the DST Root CA X3 certificate and works with Android, and the shorter one that ends with the ISRG Root X1 certificate. The shorter chain doesn’t have the Android compatibility patch:

This is a shorter chain, available to people through the API who do not need the Android compatibility of the longer chain.

Android 5.x, which Expo currently supports, needs chains with the DST Root CA X3 certificate since ISRG Root X1 is not in the device’s certificate store. For this reason, we use the longer cross-signed certificate chain.

Software that doesn’t work with the cross-signed root CA certificate will need to be updated. For software that uses Node.js, upgrading to any version of Node that is currently supported by the Node.js team (that is, 12.13.0 and up) includes a working version of OpenSSL (1.1.0 or newer) and the ISRG Root X1 certificate.

(The ISRG Root X1 certificate was added in Node 8 and OpenSSL was upgraded from 1.0.2 to 1.1.1k in Node 10 – these are both required. Using the current LTS version of Node 12 will take care of both requirements.)