Skip to content

Should we be calling SSL_CTX_add_client_CA() always when a custom CA is set? #54787

Closed as not planned

Description

@addaleax

When specifying a ca option for TLS's createSecureContext(), we call SSL_CTX_add_client_CA() since the early days of TLS in Node.js: 2a61e1c

From the docs for that function:

In most cases it is not necessary to set CA names on the client side. The list of CA names that are acceptable to the client will be sent in plaintext to the server. This has privacy implications and may also have performance implications if the list is large. This optional capability was introduced as part of TLSv1.3 and therefore setting CA names on the client side will have no impact if that protocol version has been disabled. Most servers do not need this and so this should be avoided unless required.

@tniessen @nodejs/security-triage

Activity

  1. added
    tlsIssues and PRs related to the tls subsystem.
    on Sep 5, 2024
  2. pimterry commented on Mar 25, 2026

    @pimterry
    Member

    As far as I can tell, the docs are misleading, and this chunk refers to SSL_CTX_set0_CA_list, not SSL_CTX_add_client_CA.

    In any case, in practice it seems that we never send the certificate_authorities extension in any TLS client setup I can reproduce. Example:

    const https = require('https');
    const { execSync } = require('child_process');
    const { readFileSync, unlinkSync } = require('fs');
    
    // Generate a throwaway CA to pass as the trusted CA option
    execSync(
      'openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 ' +
      '-nodes -days 1 -subj /CN=testca -keyout /tmp/ca.key -out /tmp/ca.pem 2>/dev/null'
    );
    const ca = readFileSync('/tmp/ca.pem');
    unlinkSync('/tmp/ca.key');
    unlinkSync('/tmp/ca.pem');
    
    https.get('https://testserver.host/tls/client-hello', {
      ca: [ca],
      rejectUnauthorized: false, // Our CA won't match the server's cert
    }, (res) => {
      let body = '';
      res.on('data', (d) => body += d);
      res.on('end', () => {
        const hello = JSON.parse(body);
        const caExt = hello.extensions.find((e) => e.id === 47);
        console.log('Node ' + process.version + ' (OpenSSL ' + process.versions.openssl + ')');
        if (caExt) {
          console.log('FOUND certificate_authorities extension:', JSON.stringify(caExt, null, 2));
        } else {
          console.log('certificate_authorities extension: not present');
          console.log('Found extensions:', hello.extensions.map((e) => e.name));
        }
      });
    });

    I think this can be closed.

  3. pimterry commented on Apr 20, 2026

    @pimterry
    Member

    Going to close this now, but shout if you or anybody else disagrees and I'm very happy to explore further.

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

    tlsIssues and PRs related to the tls subsystem.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions