Skip to content

httpclient: a refused server certificate says which check refused it - #358

Merged
MDA2AV merged 1 commit into
mainfrom
fix/343-verify-failure-reason
Oct 14, 2026
Merged

MDA2AV merged 1 commit into
mainfrom
fix/343-verify-failure-reason

Conversation

@MDA2AV

@MDA2AV MDA2AV commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

Fixes #343.

When the TLS client refused a server certificate, the error was the same sentence whatever the reason: certificate verify failed. SSL_get_verify_result, which holds the reason, was read only after a handshake that succeeded, where it can only be X509_V_OK. The error text was also read after await FlushCiphertextAsync() (the alert's flush). During that await other connections on the reactor run OpenSSL calls that clear the thread's error queue, which is the race #319 fixed on the server.

The failure is now captured as soon as SSL_connect fails, before the flush, in the same shape as #319's server fix. It holds the queued error and, when verification is on and the verify result is not X509_V_OK, X509_verify_cert_error_string of that result. Verification has to be on because with SSL_VERIFY_NONE OpenSSL still records a verify result, and appending it to an unrelated failure would mislead. Nothing is added per request: one SSL_get_verify_result on a failed handshake.

origin's certificate the error on main this PR
expired TLS handshake to 'origin.example.com' failed: error:0A000086:SSL routines::certificate verify failed ... certificate verify failed: certificate has expired
self-signed same ... certificate verify failed: self-signed certificate
issued by a CA the client was not given same ... certificate verify failed: unable to get local issuer certificate

Proof

The pending test tls client: a verification failure says which check failed is now a Test().

Http suite
the promoted test, with the src/ change reverted FAIL ... three different verification failures reported the same thing: TLS handshake to 'origin.example.com' failed: error:0A000086:SSL routines::certificate verify failed: 83 passed, 1 failed, 3 pending
with the fix PASS: 84 passed, 0 failed, 3 pending

The 3 pending are the tests for #342, #340 and #344, as on main (83 passed, 4 pending there). Every existing certificate verify failed assertion still passes, since the reason is appended after that text.

The error-queue half (reading before the flush) has no test of its own. Two client handshakes refused in one reactor turn, for reasons with different queue text, would pin it the way #319's server test does. The verify-result reason is read from the SSL, not from the queue, so it is right either way.

With its siblings

This batch is #356 (#342), #358 (#343), #366 (#344) and #370 (#340). They merge with each other without conflicts, checked pairwise and all four together. #358 and #366 both edit RunHandshakeAsync, in separate hunks. With all four merged, the Http suite is 88 passed, 0 failed, 0 pending.

A TLS client that refused a certificate reported only OpenSSL's "certificate verify failed",
whatever the reason. SSL_get_verify_result, which holds it, was read only after a handshake that
succeeded, where it can only be X509_V_OK. And the error text itself was read after the alert's
flush, an await during which other connections on the reactor run OpenSSL calls that clear the
thread's error queue - the race #319 fixed on the server.

The failure is now captured as soon as SSL_connect fails, before the flush: the queued error, and
when verification is on and refused the certificate, X509_verify_cert_error_string of the verify
result ("certificate has expired", "self-signed certificate", "unable to get local issuer
certificate", ...).

The pending test "tls client: a verification failure says which check failed" is now a Test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tls client: a failed certificate verification does not say which check failed

1 participant