Я пытаюсь заставить Apache httpclient5 CloseableHttpAsyncClient правильно отправлять фатальные оповещения TLS в случае сбоев, которые закрывают соединение. В соответствии с протоколом TLS v1.2: «Всякий раз, когда реализация сталкивается с состоянием, которое определяется как фатальное предупреждение, она ДОЛЖНА отправить соответствующее предупреждение перед закрытием соединения», поэтому клиент, который, например, не принимает сертификат сервера поскольку срок его действия истек, необходимо отправить фатальное предупреждение перед закрытием соединения.
Я использую httpclient5 5.2.3 с openjdk-17 с поставщиком безопасности JSSE по умолчанию.
Если я попытаюсь выполнить базовый HTTP-запрос, используя CloseableHttpAsyncClient с TLS, настроенным через системные свойства следующим образом:
try (CloseableHttpAsyncClient client = HttpAsyncClients.createDefault()) {
client.start();
SimpleHttpRequest simpleHttpRequest = SimpleHttpRequest.create(Method.GET,
URI.create("https://expired.badssl.com"));
Future<SimpleHttpResponse> future = client.execute(simpleHttpRequest, null);
SimpleHttpResponse simpleHttpResponse = future.get();
} catch (Exception e) {
e.printStackTrace();
}
Затем отправьте это на сервер с истекшим сертификатом. Как и ожидалось, клиент не принимает сертификат и закрывает соединение, но не информирует сервер, отправляя фатальное предупреждение TLS.
Требуется ли какая-либо конфигурация, чтобы httpclient5 отправлял предупреждение на сервер?
Редактировать:
Журналы при запуске примера кода:
2024-07-05T14:08:47.183+02:00 DEBUG 38589 --- [io-10666-exec-1] j.e.security : X509Certificate: Alg:SHA512withRSA, Serial:7ef6d57d52073136912...
2024-07-05T14:08:47.319+02:00 DEBUG 38589 --- [io-10666-exec-1] .c.h.i.a.InternalAbstractHttpAsyncClient : ex-0000000001 preparing request execution
2024-07-05T14:08:47.331+02:00 DEBUG 38589 --- [io-10666-exec-1] o.a.h.c.h.i.a.AsyncProtocolExec : ex-0000000001 target auth state: UNCHALLENGED
2024-07-05T14:08:47.332+02:00 DEBUG 38589 --- [io-10666-exec-1] o.a.h.c.h.i.a.AsyncProtocolExec : ex-0000000001 proxy auth state: UNCHALLENGED
2024-07-05T14:08:47.333+02:00 DEBUG 38589 --- [io-10666-exec-1] o.a.h.c.h.i.a.AsyncConnectExec : ex-0000000001 acquiring connection with route {s}->https://localhost:16270
2024-07-05T14:08:47.334+02:00 DEBUG 38589 --- [io-10666-exec-1] o.a.h.c.h.i.a.InternalHttpAsyncClient : ex-0000000001 acquiring endpoint (3 MINUTES)
2024-07-05T14:08:47.335+02:00 DEBUG 38589 --- [io-10666-exec-1] .i.n.PoolingAsyncClientConnectionManager : ex-0000000001 endpoint lease request (3 MINUTES) [route: {s}->https://localhost:16270][total available: 0; route allocated: 0 of 5; total allocated: 0 of 25]
2024-07-05T14:08:47.339+02:00 DEBUG 38589 --- [io-10666-exec-1] .i.n.PoolingAsyncClientConnectionManager : ex-0000000001 endpoint leased [route: {s}->https://localhost:16270][total available: 0; route allocated: 1 of 5; total allocated: 1 of 25]
2024-07-05T14:08:47.339+02:00 DEBUG 38589 --- [io-10666-exec-1] .i.n.PoolingAsyncClientConnectionManager : ex-0000000001 acquired ep-0000000001
2024-07-05T14:08:47.339+02:00 DEBUG 38589 --- [io-10666-exec-1] o.a.h.c.h.i.a.InternalHttpAsyncClient : ex-0000000001 acquired endpoint ep-0000000001
2024-07-05T14:08:47.339+02:00 DEBUG 38589 --- [io-10666-exec-1] o.a.h.c.h.i.a.InternalHttpAsyncClient : ep-0000000001 connecting endpoint (null)
2024-07-05T14:08:47.340+02:00 DEBUG 38589 --- [io-10666-exec-1] .i.n.PoolingAsyncClientConnectionManager : ep-0000000001 connecting endpoint to https://localhost:16270 (3 MINUTES)
2024-07-05T14:08:47.341+02:00 DEBUG 38589 --- [io-10666-exec-1] .a.h.c.h.i.n.MultihomeIOSessionRequester : localhost resolving remote address
2024-07-05T14:08:47.341+02:00 DEBUG 38589 --- [io-10666-exec-1] .a.h.c.h.i.n.MultihomeIOSessionRequester : localhost resolved to [localhost/127.0.0.1]
2024-07-05T14:08:47.342+02:00 DEBUG 38589 --- [io-10666-exec-1] .a.h.c.h.i.n.MultihomeIOSessionRequester : localhost:16270 connecting null->localhost/127.0.0.1:16270 (3 MINUTES)
2024-07-05T14:08:47.353+02:00 DEBUG 38589 --- [ient-dispatch-1] o.a.h.c.r.IOSessionImpl : c-0000000000[ACTIVE][rc:c] protocol upgrade class org.apache.hc.core5.http2.impl.nio.HttpProtocolNegotiator
2024-07-05T14:08:47.354+02:00 DEBUG 38589 --- [ient-dispatch-1] .a.h.c.h.i.n.MultihomeIOSessionRequester : localhost:16270 connected null->localhost/127.0.0.1:16270 as c-0000000000
2024-07-05T14:08:47.358+02:00 DEBUG 38589 --- [ient-dispatch-1] .i.n.DefaultManagedAsyncClientConnection : c-0000000000 start TLS
2024-07-05T14:08:47.402+02:00 DEBUG 38589 --- [ient-dispatch-1] o.a.h.c.h.s.AbstractClientTlsStrategy : Enabled protocols: [TLSv1.3, TLSv1.2]
2024-07-05T14:08:47.403+02:00 DEBUG 38589 --- [ient-dispatch-1] o.a.h.c.h.s.AbstractClientTlsStrategy : Enabled cipher suites:[TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
2024-07-05T14:08:47.403+02:00 DEBUG 38589 --- [ient-dispatch-1] o.a.h.c.h.s.AbstractClientTlsStrategy : Starting handshake (3 MINUTES)
2024-07-05T14:08:47.449+02:00 DEBUG 38589 --- [ient-dispatch-1] o.a.h.c.r.s.SSLIOSession : c-0000000000[ACTIVE][rw:c][ACTIVE][r][NEED_UNWRAP][0][0][482] Event cleared [c]
2024-07-05T14:08:47.630+02:00 DEBUG 38589 --- [ient-dispatch-1] j.e.security : X509Certificate: Alg:SHA256withRSA, Serial:10... Valid from:5/13/24, 3:54 PM, Valid until:5/13/24, 3:54 PM
2024-07-05T14:08:47.651+02:00 DEBUG 38589 --- [ient-dispatch-1] .c.h.i.a.InternalAbstractHttpAsyncClient : ex-0000000001 request failed: PKIX path validation failed: java.security.cert.CertPathValidatorException: validity check failed
2024-07-05T14:08:47.652+02:00 DEBUG 38589 --- [ient-dispatch-1] .i.n.PoolingAsyncClientConnectionManager : ep-0000000001 close IMMEDIATE
2024-07-05T14:08:47.652+02:00 DEBUG 38589 --- [ient-dispatch-1] o.a.h.c.h.i.a.InternalHttpAsyncClient : ep-0000000001 endpoint closed
2024-07-05T14:08:47.652+02:00 DEBUG 38589 --- [ient-dispatch-1] o.a.h.c.h.i.a.InternalHttpAsyncClient : ep-0000000001 discarding endpoint
2024-07-05T14:08:47.652+02:00 DEBUG 38589 --- [ient-dispatch-1] .i.n.PoolingAsyncClientConnectionManager : ep-0000000001 releasing endpoint
2024-07-05T14:08:47.652+02:00 DEBUG 38589 --- [ient-dispatch-1] .i.n.PoolingAsyncClientConnectionManager : ep-0000000001 connection released [route: {s}->https://localhost:16270][total available: 0; route allocated: 0 of 5; total allocated: 0 of 25]
2024-07-05T14:08:47.652+02:00 DEBUG 38589 --- [io-10666-exec-1] .a.h.c.h.i.a.AbstractHttpAsyncClientBase : Shutdown GRACEFUL
2024-07-05T14:08:47.654+02:00 DEBUG 38589 --- [ient-dispatch-1] o.a.h.c.r.s.SSLIOSession : c-0000000000[CLOSED][][CLOSED][r][NEED_WRAP][inbound done][][0][0][0] Close IMMEDIATE
2024-07-05T14:08:47.654+02:00 DEBUG 38589 --- [ient-dispatch-1] o.a.h.c.r.s.SSLIOSession : c-0000000000[CLOSED][][CLOSED][r][NEED_WRAP][inbound done][][0][0][0] Close GRACEFUL
2024-07-05T14:08:47.655+02:00 DEBUG 38589 --- [io-10666-exec-1] .i.n.PoolingAsyncClientConnectionManager : Shutdown connection pool GRACEFUL
2024-07-05T14:08:47.655+02:00 DEBUG 38589 --- [io-10666-exec-1] .i.n.PoolingAsyncClientConnectionManager : Connection pool shut down
Я предполагаю, что это те, о которых говорит @Robert:
2024-07-05T14:08:47.651+02:00 DEBUG 38589 --- [ient-dispatch-1] .c.h.i.a.InternalAbstractHttpAsyncClient : ex-0000000001 request failed: PKIX path validation failed: java.security.cert.CertPathValidatorException: validity check failed
2024-07-05T14:08:47.652+02:00 DEBUG 38589 --- [ient-dispatch-1] .i.n.PoolingAsyncClientConnectionManager : ep-0000000001 close IMMEDIATE
Редактировать 2:
Мне удалось заставить это работать, используя вместо этого неасинхронный клиент:
try (CloseableHttpClient client = HttpClients.createDefault()) {
ClassicHttpRequest classicHttpRequest = new BasicClassicHttpRequest(Method.GET, URI.create("https://expired.badssl.com"));
CloseableHttpResponse response = client.execute(classicHttpRequest);
client.close();
}
Это отправляет предупреждение TLS на сервер перед закрытием соединения. Логи выглядят так:
2024-07-05T17:12:17.645+02:00 DEBUG 46936 --- [io-10666-exec-1] j.e.security : X509Certificate: Alg:SHA512withRSA, Serial:7ef6d57d5207...
2024-07-05T17:12:17.732+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.i.c.InternalHttpClient : ex-0000000001 preparing request execution
2024-07-05T17:12:17.740+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.i.c.ProtocolExec : ex-0000000001 target auth state: UNCHALLENGED
2024-07-05T17:12:17.740+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.i.c.ProtocolExec : ex-0000000001 proxy auth state: UNCHALLENGED
2024-07-05T17:12:17.740+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.i.c.ConnectExec : ex-0000000001 acquiring connection with route {s}->https://localhost:16270
2024-07-05T17:12:17.741+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.i.c.InternalHttpClient : ex-0000000001 acquiring endpoint (3 MINUTES)
2024-07-05T17:12:17.742+02:00 DEBUG 46936 --- [io-10666-exec-1] h.i.i.PoolingHttpClientConnectionManager : ex-0000000001 endpoint lease request (3 MINUTES) [route: {s}->https://localhost:16270][total available: 0; route allocated: 0 of 5; total allocated: 0 of 25]
2024-07-05T17:12:17.745+02:00 DEBUG 46936 --- [io-10666-exec-1] h.i.i.PoolingHttpClientConnectionManager : ex-0000000001 endpoint leased [route: {s}->https://localhost:16270][total available: 0; route allocated: 1 of 5; total allocated: 1 of 25]
2024-07-05T17:12:17.753+02:00 DEBUG 46936 --- [io-10666-exec-1] h.i.i.PoolingHttpClientConnectionManager : ex-0000000001 acquired ep-0000000001
2024-07-05T17:12:17.753+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.i.c.InternalHttpClient : ex-0000000001 acquired endpoint ep-0000000001
2024-07-05T17:12:17.753+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.i.c.ConnectExec : ex-0000000001 opening connection {s}->https://localhost:16270
2024-07-05T17:12:17.753+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.i.c.InternalHttpClient : ep-0000000001 connecting endpoint (null)
2024-07-05T17:12:17.755+02:00 DEBUG 46936 --- [io-10666-exec-1] h.i.i.PoolingHttpClientConnectionManager : ep-0000000001 connecting endpoint to https://localhost:16270 (3 MINUTES)
2024-07-05T17:12:17.755+02:00 DEBUG 46936 --- [io-10666-exec-1] .i.i.DefaultHttpClientConnectionOperator : localhost resolving remote address
2024-07-05T17:12:17.755+02:00 DEBUG 46936 --- [io-10666-exec-1] .i.i.DefaultHttpClientConnectionOperator : localhost resolved to [localhost/127.0.0.1]
2024-07-05T17:12:17.756+02:00 DEBUG 46936 --- [io-10666-exec-1] .i.i.DefaultHttpClientConnectionOperator : localhost:16270 connecting null->localhost/127.0.0.1:16270 (3 MINUTES)
2024-07-05T17:12:17.756+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.s.SSLConnectionSocketFactory : Connecting socket to localhost/127.0.0.1:16270 with timeout 3 MINUTES
2024-07-05T17:12:17.780+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.s.SSLConnectionSocketFactory : Enabled protocols: [TLSv1.3, TLSv1.2]
2024-07-05T17:12:17.780+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.s.SSLConnectionSocketFactory : Enabled cipher suites: [TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
2024-07-05T17:12:17.780+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.s.SSLConnectionSocketFactory : Starting handshake (null)
2024-07-05T17:12:17.882+02:00 DEBUG 46936 --- [io-10666-exec-1] j.e.security : X509Certificate: Alg:SHA256withRSA, Serial:10... Valid from:5/13/24, 3:54 PM, Valid until:5/13/24, 3:54 PM
2024-07-05T17:12:17.897+02:00 DEBUG 46936 --- [io-10666-exec-1] .i.i.DefaultHttpClientConnectionOperator : localhost:16270 connection to localhost/127.0.0.1:16270 failed (class javax.net.ssl.SSLHandshakeException); terminating operation
2024-07-05T17:12:17.899+02:00 DEBUG 46936 --- [io-10666-exec-1] h.i.i.DefaultManagedHttpClientConnection : http-outgoing-0 close connection IMMEDIATE
2024-07-05T17:12:17.899+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.i.c.InternalHttpClient : ep-0000000001 endpoint closed
2024-07-05T17:12:17.899+02:00 DEBUG 46936 --- [io-10666-exec-1] o.a.h.c.h.i.c.InternalHttpClient : ep-0000000001 discarding endpoint
2024-07-05T17:12:17.899+02:00 DEBUG 46936 --- [io-10666-exec-1] h.i.i.PoolingHttpClientConnectionManager : ep-0000000001 releasing endpoint
2024-07-05T17:12:17.899+02:00 DEBUG 46936 --- [io-10666-exec-1] h.i.i.PoolingHttpClientConnectionManager : ep-0000000001 connection is not kept alive
2024-07-05T17:12:17.900+02:00 DEBUG 46936 --- [io-10666-exec-1] h.i.i.PoolingHttpClientConnectionManager : ep-0000000001 connection released [route: {s}->https://localhost:16270][total available: 0; route allocated: 0 of 5; total allocated: 0 of 25]
2024-07-05T17:12:17.900+02:00 DEBUG 46936 --- [io-10666-exec-1] h.i.i.PoolingHttpClientConnectionManager : Shutdown connection pool GRACEFUL
2024-07-05T17:12:17.900+02:00 DEBUG 46936 --- [io-10666-exec-1] h.i.i.PoolingHttpClientConnectionManager : Connection pool shut down
@ ok2c, есть ли способ заставить это работать и для асинхронного?
Редактировать 3:
Wireshark с CloseableHttpAsyncClient:
Wireshark с CloseableHttpClient:
Я также могу видеть эти журналы на своем сервере, подтверждающие, что он получил фатальное предупреждение:
javax.net.ssl|DEBUG|12|https-jsse-nio-16270-exec-2|2024-07-08 13:57:58.885 CEST|SSLEngineInputRecord.java:176|Raw read (
0000: 15 03 03 00 02 02 2D ......-
)
javax.net.ssl|DEBUG|12|https-jsse-nio-16270-exec-2|2024-07-08 13:57:58.886 CEST|SSLEngineInputRecord.java:213|READ: TLSv1.2 alert, length = 2
javax.net.ssl|DEBUG|12|https-jsse-nio-16270-exec-2|2024-07-08 13:57:58.887 CEST|Alert.java:238|Received alert message (
"Alert": {
"level" : "fatal",
"description": "certificate_expired"
}
)
javax.net.ssl|ERROR|12|https-jsse-nio-16270-exec-2|2024-07-08 13:57:58.887 CEST|TransportContext.java:370|Fatal (CERTIFICATE_EXPIRED): Received fatal alert: certificate_expired (
"throwable" : {
javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_expired
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:365)
at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:293)
at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:204)
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)
at java.base/sun.security.ssl.SSLEngineImpl.decode(SSLEngineImpl.java:736)
at java.base/sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:691)
at java.base/sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:506)
at java.base/sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:482)
at java.base/javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:679)
at org.apache.tomcat.util.net.SecureNioChannel.handshakeUnwrap(SecureNioChannel.java:479)
at org.apache.tomcat.util.net.SecureNioChannel.handshake(SecureNioChannel.java:213)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1719)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:840)}
)
Но при использовании CloseableHttpAsyncClient таких журналов на сервере нет.
Журналы отладки TLS клиента при использовании CloseableHttpClient также отличаются, в них указано критическое предупреждение:
javax.net.ssl|DEBUG|12|http-nio-10666-exec-1|2024-07-08 14:15:31.852 CEST|SSLSocketOutputRecord.java:71|WRITE: TLSv1.2 alert(certificate_expired), length = 2
javax.net.ssl|DEBUG|12|http-nio-10666-exec-1|2024-07-08 14:15:31.852 CEST|SSLSocketOutputRecord.java:85|Raw write (
0000: 15 03 03 00 02 02 2D ......-
)
javax.net.ssl|DEBUG|12|http-nio-10666-exec-1|2024-07-08 14:15:31.852 CEST|SSLSocketImpl.java:1759|close the underlying socket
javax.net.ssl|DEBUG|12|http-nio-10666-exec-1|2024-07-08 14:15:31.852 CEST|SSLSocketImpl.java:1785|close the SSL connection (passive)
Я не уверен, что JSSE действительно предоставляет потребителям API уровня рукопожатия, и здесь HttpClient может многое сделать. В любом случае, не стесняйтесь поднимать проблему с проектом и прикреплять к заявке полный проводной/контекстный журнал сеанса, а также журнал рукопожатий TLS.
@ ok2c Даже если реализация TLS является частью JRE, похоже, что соединение закрывается напрямую после неудачной проверки сертификата. Если TCP-соединение закрыто Apache httpclient, реализация JRE TLS не сможет отправить предупреждение.
@ Роберт Кажется, как?
@ ok2c Если вы выполните пример кода, вы увидите вывод журнала, в котором упоминается немедленное закрытие соединения после неудачной проверки сертификата. но я не уверен на 100%, что журнал взят из Apache httpclient, поскольку я не использую Commons-Log, поэтому имя класса отсутствует в журнале.
@Роберт, я добавил логи в сообщение, похоже, это из классов Apache httpclient, если вы имели в виду именно их.
@Robert Немедленное отключение пула соединений не влияет на рукопожатие TLS.
@ ok2c Я думаю, вы смотрите не на ту строку журнала. Я имею в виду вот это, похоже, о связи: PoolingAsyncClientConnectionManager : ep-0000000001 close IMMEDIATE
Пожалуйста, попробуйте этот набор изменений локально и дайте мне знать, если решит проблему для вас.
Нет никакой гарантии, что это заставит JSSE сгенерировать фатальное предупреждение, но, по крайней мере, HttpClient попытается корректно закрыть сеанс TLS.
Обновление: нашел причину проблемы и обновил свой патч.
Теперь я вижу в журналах отладки TLS
javax.net.ssl|WARNING|0F|requester-dispatch-1|2024-07-07 11:39:23.907 CEST|SSLEngineOutputRecord.java:168|outbound has closed, ignore outbound application data
javax.net.ssl|FINE|0F|requester-dispatch-1|2024-07-07 11:39:23.907 CEST|SSLEngineOutputRecord.java:505|WRITE: TLS12 alert, length = 2
javax.net.ssl|FINE|0F|requester-dispatch-1|2024-07-07 11:39:23.907 CEST|SSLEngineOutputRecord.java:523|Raw write (
0000: 15 03 03 00 02 02 2D ......-
)
2024-07-07 11:39:23,903 ERROR [requester-dispatch-1][org.apache.hc.core5.http.connection] c-0000000000[ACTIVE][r:r][ACTIVE][rw][NEED_WRAP][inbound done][][347][0][0] PKIX path validation failed: java.security.cert.CertPathValidatorException: validity check failed
javax.net.ssl.SSLHandshakeException: PKIX path validation failed: java.security.cert.CertPathValidatorException: validity check failed
Вам нужно будет собрать HttpCore из исходного кода и заставить HttpClient использовать этот снимок для проверки исправления.
Спасибо за вашу помощь @ ok2c, однако это не сработало, предупреждение TLS по-прежнему отсутствует. Однако я нашел кое-что интересное. Неасинхронный клиент действительно отправляет предупреждение TLS, дополнительную информацию см. В моих изменениях.
Как я уже говорил ранее, JSSE, поставляемый с Java 1.8, не предоставляет API для явного управления рукопожатием TLS. Я посмотрю еще раз, но, скорее всего, вам нужно сообщить об этой проблеме вышестоящим специалистам JSSE.
Спасибо, что продолжаете изучать это! Я тестировал ваш новый патч и также вижу журналы отладки TLS, однако, когда я проверяю пакеты с помощью Wireshark, предупреждение TLS по-прежнему не отправляется, я добавил в сообщение изображения с выводом Wireshark и отличиями от использования CloseableHttpClient и CloseableHttpAsyncClient
Я также добавил журналы отладки TLS при использовании неасинхронного клиента, они взяты из SSLSocketOutputRecord
вместо SSLEngineOutputRecord
, и в них также указано, что это предупреждение certificate_expired.
Я подправил набор изменений, чтобы попытаться записать данные протокола, которые могут застрять в выходном буфере протокола. Если это не поможет, пожалуйста, обсудите этот вопрос с людьми из JSSE.
Теперь все работает! Большое спасибо!
Цитата: «Всякий раз, когда реализация сталкивается с состоянием, которое определяется как фатальное предупреждение, она ДОЛЖНА отправить соответствующее предупреждение перед закрытием соединения». взято из «RFC 5246 — TLS v1.2» в главе «Предупреждения об ошибках» (7.2.2), и поскольку клиент не принимает сертификат, это фатальное предупреждение.