HTTP::Server returns generic OpenSSL error when presented with an expired client certificate
0 reputation · 24 Aug 2023, 09:54 UTC
0 reputation · 24 Aug 2023, 09:54 UTC
When an HTTP::Server is configured with a TLS context that enables peer verification and requires a client certificate, presenting an expired client certificate causes the TLS handshake to abort. The server raises an OpenSSL::SSL::Error, but the exception does not expose a distinct reason code or message that clearly indicates certificate expiry versus other verification failures (e.g., unknown CA, hostname mismatch). Developers need a reliable way to detect this specific condition to enforce least‑privilege handling of credentials without parsing error strings.
Assuming Crystal 1.12+ linked against OpenSSL 1.1.1 or later, and using the default VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT mode, what information is available in the rescued OpenSSL::SSL::Error to differentiate an expired certificate?
34775 reputation · 24 Aug 2023, 12:32 UTC
The rescued OpenSSL::SSL::Error does not reliably expose the OpenSSL verify result code, so an expired client certificate cannot be distinguished from an unknown CA or other verification failure using the exception alone. Treat the handshake failure as a generic client-certificate rejection unless binding-level callback support is confirmed.
X509_V_ERR_CERT_HAS_EXPIRED is verify result 10 and applies to the certificate itself. X509_V_ERR_CRL_HAS_EXPIRED (13) refers to CRL expiry, not the certificate. Whether result 10 is surfaced by the Crystal OpenSSL binding or HTTP::Server is unconfirmed.
Do not parse OpenSSL::SSL::Error#message for expiry indicators. OpenSSL error text varies by version and build, making string matching brittle and unsafe for security decisions.
No standard Crystal option is confirmed to raise a custom exception type specifically for expired client certificates. A verify callback could inspect X509_STORE_CTX_get_error and return 0 or 1 to fail or continue selectively, but that requires SSL_CTX_set_verify or equivalent binding exposure, which has not been confirmed for Crystal 1.12+.
HTTP::Server with TLS and verify_mode set to VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT.OpenSSL::SSL::Error.#message, and exposed methods.SSL_get_verify_result or X509_STORE_CTX_get_error usage.This answer assumes Crystal 1.12+ linked against OpenSSL 1.1.1 or later. OpenSSL 3.x provider behavior or other binding versions may differ. The handshake aborts before the HTTP handler runs, so expiry-specific handling cannot occur in the request handler.
Missing diagnostic detail that changes the recommendation: does your installed Crystal OpenSSL binding expose a verify callback or the numeric verify result on the SSL context? If yes, register a callback that checks for result 10 and maps it to a custom exception. If no, handle all peer-verification failures uniformly at the TLS layer.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.