Skip to content

tls: read the peer certificate chain without consuming it - #65602

Closed
tgies wants to merge 1 commit into
nodejs:mainfrom
tgies:tls-peer-cert-chain
Closed

tls: read the peer certificate chain without consuming it#65602
tgies wants to merge 1 commit into
nodejs:mainfrom
tgies:tls-peer-cert-chain

Conversation

@tgies

@tgies tgies commented Aug 27, 2026

Copy link
Copy Markdown

Server-side getPeerCertificate(true) stopped returning the client certificate's issuer chain in v26.8.0. issuerCertificate became undefined.

The root cause actually predates this regression; X509Certificate::GetPeerCert() walked the stack from SSL_get_peer_cert_chain() with sk_X509_delete(), so the first read destroyed the chain for every later read. #64677 added an internal call to getPeerX509Certificate() in onServerSocketSecure() to detect presence of a certificate, so now the mutating read happens every time.

This changes getPeerX509Certificate() to read the chain non-destructively, copying issuers with X509_dup, and replaces the presence check with a hasPeerCertificate() binding.

Fixes: #65579
Refs: #64677

X509Certificate::GetPeerCert() built the certificate objects by deleting
entries from the stack returned by SSL_get_peer_cert_chain(), which is
owned by the SSL session. The first read emptied it, so any later read
by getPeerCertificate() or getPeerX509Certificate(), on either peer, saw
a truncated chain or nothing. Copy each issuer with X509_dup instead and
leave the session's stack untouched.

onServerSocketSecure() only called getPeerX509Certificate() to check
whether a peer certificate was present, building the whole chain on every
server handshake; that is what first exposed the destructive read. Use a
lightweight hasPeerCertificate() binding for the presence check.

Signed-off-by: Tony Gies <tgies@tgies.net>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/crypto
  • @nodejs/net

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. labels Aug 27, 2026
@tgies

tgies commented Aug 27, 2026

Copy link
Copy Markdown
Author

BTW, the argument for adding the hasPeerCertificate() binding even after making getPeerX509Certificate() nondestructive is mainly that it's expensive and unnecessary to call getPeerX509Certificate() here anyway. That builds the whole cert chain and the JS X509Certificate wrapper objects on every handshake just so we can test != null.

tgies added a commit to tgies/client-certificate-auth that referenced this pull request Aug 28, 2026


Node 26.8.0 stopped exposing the server-side peer chain via
getPeerCertificate(true) (nodejs/node#65579, fix nodejs/node#65602), so the
socket-path includeChain test failed there. It now probes the running Node
once and asserts the issuerCertificate chain only when the runtime still
exposes it. README and the troubleshooting guide note the regression and the
allowFingerprints/allowCA/header-path workarounds.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tls: server getPeerCertificate(true) drops the peer certificate chain since v26.8.0 (regressed by #64677)

2 participants