sdk icon indicating copy to clipboard operation
sdk copied to clipboard

Allow dart:io SecurityContext to override BoringSSL Extended Key Usage (EKU) check for mTLS

Open cpswan opened this issue 6 months ago • 1 comments

When reporting an issue, please include:

  • Dart 3.8.1
  • Linux

Why is this needed?

At present TLS certificates issued by popular public Certificate Authorities (CAs) such as LetsEncrypt and ZeroSSL [1] come with Extended Key Usage (EKU) attributes for Server Auth and Client Auth. This means that services can present their certificates to each other for mutual transport layer security (mTLS) and all the relevant checks pass.

The Google Chrome team have announced a change in their CA policy to Promote use of Dedicated TLS Server Authentication PKI Hierarchies that forces CAs to only issue certs with the Server Auth EKU (id-kp-serverAuth). As a result LetsEncrypt have announced "Ending TLS Client Authentication Certificate Support in 2026". Meaning that certificates will no longer have the Client Auth EKU from 13 May 2026. It's reasonable to expect that other CAs will follow a similar process, and in a similar time frame to meet the Chrome policy deadline of 15 June 2026.

What happens in Dart today?

When the client side of an mTLS connection (between two peer services) presents a certificate that only contains the Server Auth EKU a HandshakeException is thrown:

HandshakeException: Handshake error in server (OS Error:
        CERTIFICATE_VERIFY_FAILED: unsupported certificate purpose(handshake.cc:295))

This happens irrespective of whether requireClientCertificate: is set to true or false. When requestClientCertificate: true and a client cert is presented then BoringSSL checks the EKUs, and if it just finds Server Auth it throws an error [2].

There is no way to override this behaviour as the exception is thrown before the SecureSocket object is returned.

Requested change

By setting requireClientEKUVerify: false it should be possible for the securityContext object to pass a flag to BoringSSL such that the EKU check is not required.

Example code

The cpswan/dart_mtls repo contains an example tls_socket_server.dart and tls_socket_client.dart implementation along with (self signed) certificates with various EKUs:

  • client.crt: Server Auth and Client Auth
  • client.noext.crt: none
  • client.web.crt: Server Auth

The service address atsign.test is expected to resolve to localhost (127.0.0.1).

Notes

[1] Google's own public CA defaults to issuing certificates with just the Server Auth EKU, but (for now) it's trivial to modify the Certificate Signing Request (CSR) to get a certificate that also has the Client Auth EKU. [2] This check isn't super rigorous, so if a cert with no EKUs is presented that passes the test. So it's not precisely that it's ensuring that Client Auth is present but rather that if there are EKUs then Client Auth must be one of them.

cpswan avatar Jun 10 '25 11:06 cpswan

cc @brianquinlan (or, whoever is most knowledgable about our use of BoringSSL)

devoncarew avatar Jun 10 '25 16:06 devoncarew