[
https://issues.apache.org/jira/browse/CAMEL-25649?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen resolved CAMEL-25649.
---------------------------------
Resolution: Fixed
> camel-netty - hostnameVerification never verifies: the SSLEngine is created
> without a peer, so enabling it fails every handshake
> --------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-25649
> URL: https://issues.apache.org/jira/browse/CAMEL-25649
> Project: Camel
> Issue Type: Bug
> Components: camel-netty
> Reporter: Andrea Cosentino
> Assignee: Andrea Cosentino
> Priority: Major
> Fix For: 4.24.0
>
>
> h3. Summary
> {{camel-netty}}'s {{hostnameVerification}} option cannot verify anything.
> When it is enabled the TLS handshake fails for *every* certificate, matching
> or not, with:
> {noformat}
> javax.net.ssl.SSLHandshakeException: ...
> caused by java.security.cert.CertificateException: Hostname or IP address
> is undefined.
> {noformat}
> So the option has never worked since it was added in CAMEL-16315 (3.7.4 /
> 3.9.0). An operator who turns it on to secure a TLS producer gets a hard
> connection failure and has no working way to opt in.
> h3. Cause
> {{DefaultClientInitializerFactory.configureClientSSLOnDemand()}} creates the
> engine with the *no-arg* factory method and then asks for host name
> verification:
> {code:java}
> SSLEngine engine = sslContext.createSSLEngine(); // no peer host, no
> peer port
> engine.setUseClientMode(true);
> if (producer.getConfiguration().isHostnameVerification()) {
> SSLParameters sslParams = engine.getSSLParameters();
> sslParams.setEndpointIdentificationAlgorithm("HTTPS");
> engine.setSSLParameters(sslParams);
> }
> {code}
> The JDK's identity check reads the host from the session rather than from the
> connection: {{X509TrustManagerImpl.checkIdentity}} calls
> {{SSLSession.getPeerHost()}} and hands the result to
> {{HostnameChecker.match}}, whose first statement is
> {code:java}
> if (expectedName == null) {
> throw new CertificateException("Hostname or IP address is undefined.");
> }
> {code}
> {{getPeerHost()}} is only populated when the engine is created with
> {{createSSLEngine(host, port)}}. With the no-arg form it is {{null}}, so the
> check cannot run and the handshake is aborted instead.
> h3. Reproduction
> A client/server {{SSLEngine}} pair driven in memory against a certificate
> whose only SAN is {{DNS:localhost}}, on JDK 21.0.10:
> || client engine || peerHost || result ||
> | {{createSSLEngine()}} - what camel-netty does | {{null}} |
> {{CertificateException: Hostname or IP address is undefined.}} |
> | {{createSSLEngine("localhost", 443)}} - what camel-netty-http does |
> {{localhost}} | handshake completes |
> | {{createSSLEngine("other.example", 443)}} | {{other.example}} |
> {{CertificateException: No subject alternative DNS name matching
> other.example found.}} |
> The third row is the control: it shows verification really is active in the
> second row rather than passing vacuously.
> h3. Why camel-netty-http is not affected
> CAMEL-14070 gave {{camel-netty-http}} SNI support in 4.x by creating its
> client engine as {{createSSLEngine(uri.getHost(), uri.getPort())}}. When
> CAMEL-20812 later copied the host name verification block into
> {{HttpClientInitializerFactory}}, it landed on an engine that already carried
> the peer, so there it works. The original in {{camel-netty}} never did -
> which is also why CAMEL-20812's reporter described the {{camel-netty}}
> implementation as the working one.
> h3. Proposed fix
> Create the client engine with the configured peer when verification is
> enabled, as {{camel-netty-http}} does. Keeping the no-arg form when the
> option is off confines the change to operators who opted in - and for them
> the current behaviour is a hard failure, so there is no regression to cause.
> Attaching the peer also makes the JDK send SNI, which is why it is better not
> to do it on the default path.
> h3. Consumer side - separate question
> {{DefaultServerInitializerFactory}} applies the same block to the consumer's
> engine, also created with the no-arg form:
> * With {{clientMode=true}} the consumer *is* a TCP client, so the same fix
> applies.
> * In server mode, {{endpointIdentificationAlgorithm=HTTPS}} asks the JDK to
> match the *client's* certificate against a host name the server does not
> have. Whenever a client certificate is actually presented this fails the
> handshake the same way; and matching a client certificate against the
> client's address is not a meaningful control. The honest options are to not
> apply the algorithm in server mode, or to document the option as client-side
> only.
> Same block is in {{camel-netty-http}}'s {{HttpServerInitializerFactory}} and
> {{HttpServerSharedInitializerFactory}}.
> This is a functional defect rather than an exposure: the broken state fails
> closed, and the insecure default ({{hostnameVerification=false}}) is
> pre-existing, documented, and already carries {{security = "insecure:ssl"}}.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)