Skip to content

TLS 'secureConnect' event fires twice in case of TLS renegotiation #54362

Open
@mxschmitt

Description

@mxschmitt

Version

22.6.0

Platform

Microsoft Windows NT 10.0.22631.0 x64

also reproducible under:

Darwin 23.6.0 Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6000 arm64

Subsystem

tls

What steps will reproduce the bug?

Generate the certs and then run the server.js and then the client.js.

mkdir certs
cd certs
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out cert.pem -subj "/C=US/ST=State/L=City/O=Organization/OU=Unit/CN=localhost"
openssl req -newkey rsa:2048 -nodes -keyout client_key.pem -out client_req.pem -subj "/C=US/ST=State/L=City/O=Organization/OU=Unit/CN=client"
openssl x509 -req -in client_req.pem -CA cert.pem -CAkey key.pem -CAcreateserial -out client_cert.pem -days 365  -subj "/C=US/ST=State/L=City/O=Organization/OU=Unit/CN=client"
// client.js
const tls = require('tls');
const fs = require('fs');

const tlsSocket = tls.connect({
    host: '127.0.0.1',
    port: 8443,
    servername: 'localhost',
    rejectUnauthorized: false,
    key: fs.readFileSync('certs/client_key.pem'),
    cert: fs.readFileSync('certs/client_cert.pem'),
});
tlsSocket.on('secureConnect', () => {
    console.log('secureConnect event fired!');
    tlsSocket.write('hi');
});
tlsSocket.on('data', (data) => {
    console.log(`Received: ${data.toString().trim()}`);
});
// server.js
const tls = require('tls');
const fs = require('fs');

const options = {
    key: fs.readFileSync('certs/key.pem'),
    cert: fs.readFileSync('certs/cert.pem'),
    requestCert: false,
    rejectUnauthorized: false,
    secureProtocol: 'TLSv1_2_method', // Force TLS 1.2
};

const server = tls.createServer(options, (socket) => {
    console.log(`Secure connection established using ${socket.getProtocol()}`);
    socket.on('data', (data) => {
        console.log('Received:', data.toString());
    });
    socket.write('\n_renegotiating\n');
    socket.renegotiate({
        requestCert: true,
        rejectUnauthorized: true
    }, (err) => {
        if (err) {
            console.error('Renegotiation error:', err);
        } else {
            console.log('Renegotiation successful');
            socket.end('\n_renegotiated\n');
        }
    });
});

server.listen(8443, () => {
    console.log('Server listening on port 8443');
});

server.on('tlsClientError', (err) => {
    console.error('TLS Client Error:', err);
});

How often does it reproduce? Is there a required condition?

  • TLS 1.2.
  • server re-negotiates the TLS connections with client certificates.

What is the expected behavior? Why is that the expected behavior?

secureConnect events fires once.

What do you see instead?

secureConnect event fires multiple times.

Additional information

  • This got surfaced when using IIS on Windows Server. Windows Server does use negotiation when using client-certificates.

  • When passing a callback to tls.connect (which is similar to secureConnect this won't surface, since its using once() internally.

Logs:

env➜  playwright git:(main) ✗ NODE_DEBUG=tls node client.js
TLS 25844: client _init handle? true
TLS 25844: client initRead handle? true buffered? false
TLS 25844: client _start handle? true connecting? false requestOCSP? false
TLS 25844: client onhandshakedone
TLS 25844: client _finishInit handle? true alpn false servername localhost
TLS 25844: client emit secureConnect. rejectUnauthorized: false, authorizationError: DEPTH_ZERO_SELF_SIGNED_CERT
secureConnect event fired! Error
    at TLSSocket.<anonymous> (/Users/maxschmitt/Developer/playwright/client.js:13:47)
    at TLSSocket.emit (node:events:520:28)
    at TLSSocket.onConnectSecure (node:_tls_wrap:1716:8)
    at TLSSocket.emit (node:events:520:28)
    at TLSSocket._finishInit (node:_tls_wrap:1078:8)
    at ssl.onhandshakedone (node:_tls_wrap:864:12)
Received: _renegotiating
TLS 25844: client onhandshakedone
TLS 25844: client _finishInit handle? true alpn false servername localhost
TLS 25844: client emit secureConnect. rejectUnauthorized: false, authorizationError: DEPTH_ZERO_SELF_SIGNED_CERT
secureConnect event fired! Error
    at TLSSocket.<anonymous> (/Users/maxschmitt/Developer/playwright/client.js:13:47)
    at TLSSocket.emit (node:events:520:28)
    at TLSSocket.onConnectSecure (node:_tls_wrap:1716:8)
    at TLSSocket.emit (node:events:520:28)
    at TLSSocket._finishInit (node:_tls_wrap:1078:8)
    at ssl.onhandshakedone (node:_tls_wrap:864:12)

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