I've set up a server that accepts HTTPS connections with a custom CA certificate. I've installed it on my Samsung Galaxy A50, and can now access the server without warnings in Chrome. Now, I'd like to limit access to clients authenticated with mTLS, where they submit client certificates.

So far, it works properly on desktop browsers, refusing a TLS connection when a client certificate isn't provided. I now need to access this on the Android phone.

I usually use Firefox on that phone, but Firefox does not support client certificates yet. Chrome does, however: when I access https://certauth.idrix.fr with the client certificate installed, the certificate choice popup does appear. It doesn't do that for my server, however: instead, it shows a ERR_BAD_SSL_CLIENT_AUTH_CERT (WEBSITE didn't accept your login certificate, or one may not have been provided. Try contacting the system admin.), and doesn't prompt me to pick the certificate.

From my testing, the problem doesn't seem to be specific to my server: the same happens with mTLS configs for Nginx, Apache HTTPd, and Traefik. However, the https://certauth.idrix.fr server is somehow special, because the mTLS works for it.

How do I get Chrome to do this prompt? If I can't, what other browsers support client certificates? (It seems that very few do.)

1 Answer 1


It turns out that when a TLS server asks for a certificate, it sends a list of certificate authorities that it will accept a certificate from. Desktop browsers either ignore that list, or resolve the available client certificates up to the root. However, on Android, the dropdown is filtered by this list of certificates accepted by the server. Crucially, the certificate being referenced (i.e. directly trusted) by the server must be included in the .p12 bundle on the Android.

In my case, I was managing my CA with step-cli. When that generated a certificate, in the output file, it only included the leaf certificate and the certificate of the intermediate CA, not the root CA. So, when bundled into a .p12 file to import into Android, the Android didn't know the root certificate, so it couldn't determine that the certificate it had available could work with the server.

Here are the possible solutions to this problem:

  1. Have the server accept all certificates, and verify them later: On nginx, there is the option ssl_verify_client optional_no_ca. This sends a certificate requests, but establishes the connection even if it was not provided, and does not verify the certificate. Then, the application can be set up to get the certificate from a proxy header and verify it manually. This allows the user to pick any of the certificates it has on file, which is bad for UX as it allows the user to pick the wrong one.
  2. Add the intermediate CA to the verification list on the server: by doing this, it adds the intermediate CA's signature to the list of CAs accepted by the server during the handshake, which will allow Android to present the certificate because the bundle contains the CA being referenced by the certificate request. However, this means that revoking the intermediate CA will have to be done by manually distrusting it on the server.
  3. (Best option!) When creating a .p12 certificate bundle to import into Android, make sure that the .crt file contains the root CA's certificate. Then, the Android will again know that this certificate is actually desired by the server.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .