Skip to content

Certificates from mix phx.gen.cert are rejected by Chrome #6847

Description

@bartblast

Environment

  • Elixir version (elixir -v): Elixir 1.20.0 (compiled with Erlang/OTP 29)
  • Phoenix version (mix deps): 1.8.9, and lib/mix/tasks/phx.gen.cert.ex is the same on main
  • Operating system: macOS 15, Chrome 153

Actual behavior

Chrome refuses to connect to a server using a certificate from mix phx.gen.cert. The tab shows ERR_SSL_PROTOCOL_ERROR. Because it's a protocol error rather than a trust warning, there is no "proceed anyway", and --ignore-certificate-errors doesn't get past it either. On the server, :ssl logs TLS :server: In state :certify received CLIENT ALERT: Fatal - Decode Error (:wait_finished over TLS 1.3). curl and OpenSSL connect fine.

The certificate's public key info names the key type with the rsaEncryption algorithm identifier but leaves its parameters out. RFC 3279 §2.3.1 says they "MUST have ASN.1 type NULL". BoringSSL, Chrome's TLS library, enforces that when decoding the key (rsa_pub_decode in crypto/evp/p_rsa.cc returns EVP_R_DECODE_ERROR), so Chrome aborts the handshake.

$ openssl asn1parse -in priv/cert/selfsigned.pem | grep -A1 rsaEncryption
  213:d=4  hl=2 l=   9 prim: OBJECT            :rsaEncryption
  224:d=3  hl=4 l= 271 prim: BIT STRING

A certificate from openssl req -x509 has a NULL between those two lines.

The parameters went away in #6332, which fixed the OTP 28 crash from #6319 by dropping parameters: :NULL from both the signature algorithm and the public key algorithm. Only the signature one needed it. On OTP 28+ the signature algorithm's parameters are an open type, so the :NULL atom no longer encodes there, which is the badmatch in #6319. The public key algorithm's parameters are a plain NULL type and still encode fine.

To find which field Chrome cares about, I re-signed the certificate from Mix.Tasks.Phx.Gen.Cert.certificate_and_key/3 four ways, served each one, and loaded it in Chrome:

                            signature algorithm   public key algorithm   Chrome
as generated                no NULL               no NULL                fails
NULL on signature only      NULL                  no NULL                fails
NULL on public key only     no NULL               NULL                   loads
NULL on both                NULL                  NULL                   loads

Reproduction from scratch:

mix phx.new demo && cd demo
mix phx.gen.cert

Add the https: config the task prints to config/dev.exs, mix phx.server, open https://localhost:4001 in Chrome.

Expected behavior

The certificate loads in Chrome like one from OpenSSL does. Putting the parameters back on the public key algorithm only is enough:

       subjectPublicKeyInfo:
         otp_subject_public_key_info(
-          algorithm: public_key_algorithm(algorithm: @rsaEncryption),
+          algorithm: public_key_algorithm(algorithm: @rsaEncryption, parameters: :NULL),
           subjectPublicKey: public_key
         ),

I checked that this encodes with :public_key.pkix_sign/2 on OTP 25.3, 27.2, 28.1, 28.5 and 29.0, and that the pre-#6332 form (both :NULL) still crashes on 28.1, 28.5 and 29.0, so the OTP 28 fix stays intact. The signature algorithm can stay as it is: Chrome accepts it without parameters, and RFC 4055 §5 requires implementations to accept that form.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions