Skip to content

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

Open
@cpswan

Description

@cpswan

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.

Metadata

Metadata

Assignees

Labels

area-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.library-_httptriagedIssue has been triaged by sub team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions