1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.client5.testing.sync;
29
30 import static org.hamcrest.MatcherAssert.assertThat;
31
32 import java.io.IOException;
33 import java.net.InetAddress;
34 import java.net.Socket;
35 import java.util.Objects;
36
37 import javax.net.ssl.HostnameVerifier;
38 import javax.net.ssl.SSLContext;
39 import javax.net.ssl.SSLException;
40 import javax.net.ssl.SSLHandshakeException;
41 import javax.net.ssl.SSLPeerUnverifiedException;
42 import javax.net.ssl.SSLSession;
43 import javax.net.ssl.SSLSocket;
44
45 import org.apache.hc.client5.http.protocol.HttpClientContext;
46 import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
47 import org.apache.hc.client5.http.ssl.HostnameVerificationPolicy;
48 import org.apache.hc.client5.http.ssl.HttpsSupport;
49 import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
50 import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
51 import org.apache.hc.client5.testing.SSLTestContexts;
52 import org.apache.hc.core5.http.HttpHost;
53 import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
54 import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
55 import org.apache.hc.core5.io.CloseMode;
56 import org.apache.hc.core5.ssl.SSLContexts;
57 import org.apache.hc.core5.ssl.TrustStrategy;
58 import org.hamcrest.CoreMatchers;
59 import org.hamcrest.MatcherAssert;
60 import org.hamcrest.Matchers;
61 import org.junit.jupiter.api.AfterEach;
62 import org.junit.jupiter.api.Assertions;
63 import org.junit.jupiter.api.Test;
64
65
66
67
68 class TestDefaultClientTlsStrategy {
69
70 private HttpServer server;
71
72 @AfterEach
73 void shutDown() {
74 if (this.server != null) {
75 this.server.close(CloseMode.GRACEFUL);
76 }
77 }
78
79 static class TestX509HostnameVerifier implements HostnameVerifier {
80
81 private boolean fired;
82
83 @Override
84 public boolean verify(final String host, final SSLSession session) {
85 this.fired = true;
86 return true;
87 }
88
89 public boolean isFired() {
90 return this.fired;
91 }
92
93 }
94
95 @Test
96 void testBasicSSL() throws Exception {
97
98 this.server = ServerBootstrap.bootstrap()
99 .setSslContext(SSLTestContexts.createServerSSLContext())
100 .setRequestRouter((r, c) -> null)
101 .create();
102
103 this.server.start();
104
105 final HttpClientContext context = HttpClientContext.create();
106 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
107 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
108 SSLTestContexts.createClientSSLContext(), hostVerifier);
109 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
110 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
111 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
112 socket,
113 target.getHostName(),
114 target.getPort(),
115 null,
116 context)) {
117 final SSLSession sslsession = sslSocket.getSession();
118
119 Assertions.assertNotNull(sslsession);
120 Assertions.assertTrue(hostVerifier.isFired());
121 }
122 }
123 }
124
125 @Test
126 void testBasicDefaultHostnameVerifier() throws Exception {
127
128 this.server = ServerBootstrap.bootstrap()
129 .setSslContext(SSLTestContexts.createServerSSLContext())
130 .setRequestRouter((r, c) -> null)
131 .create();
132
133 this.server.start();
134
135 final HttpClientContext context = HttpClientContext.create();
136 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext());
137 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
138 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
139 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
140 socket,
141 target.getHostName(),
142 target.getPort(),
143 null,
144 context)) {
145 final SSLSession sslsession = sslSocket.getSession();
146
147 Assertions.assertNotNull(sslsession);
148 }
149 }
150 }
151
152 @Test
153 void testClientAuthSSL() throws Exception {
154
155 this.server = ServerBootstrap.bootstrap()
156 .setSslContext(SSLTestContexts.createServerSSLContext())
157 .setRequestRouter((r, c) -> null)
158 .create();
159
160 this.server.start();
161
162 final HttpClientContext context = HttpClientContext.create();
163 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
164 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
165 SSLTestContexts.createClientSSLContext(), hostVerifier);
166 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
167 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
168 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
169 socket,
170 target.getHostName(),
171 target.getPort(),
172 null,
173 context)) {
174 final SSLSession sslsession = sslSocket.getSession();
175
176 Assertions.assertNotNull(sslsession);
177 Assertions.assertTrue(hostVerifier.isFired());
178 }
179 }
180 }
181
182 @Test
183 void testClientAuthSSLFailure() throws Exception {
184
185 this.server = ServerBootstrap.bootstrap()
186 .setSslContext(SSLTestContexts.createServerSSLContext())
187 .setSslSetupHandler(sslParameters -> sslParameters.setNeedClientAuth(true))
188 .setRequestRouter((r, c) -> null)
189 .create();
190
191 this.server.start();
192
193 final HttpClientContext context = HttpClientContext.create();
194 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
195 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
196 SSLTestContexts.createClientSSLContext(), hostVerifier);
197 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
198 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
199 Assertions.assertThrows(IOException.class, () -> {
200 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
201 socket,
202 target.getHostName(),
203 target.getPort(),
204 null,
205 context)) {
206 final SSLSession sslsession = sslSocket.getSession();
207
208 Assertions.assertNotNull(sslsession);
209 Assertions.assertTrue(hostVerifier.isFired());
210 sslSocket.getInputStream().read();
211 }
212 });
213 }
214 }
215
216 @Test
217 void testSSLTrustVerification() throws Exception {
218
219 this.server = ServerBootstrap.bootstrap()
220 .setSslContext(SSLTestContexts.createServerSSLContext())
221 .setRequestRouter((r, c) -> null)
222 .create();
223
224 this.server.start();
225
226 final HttpClientContext context = HttpClientContext.create();
227
228 final SSLContext defaultSslContext = SSLContexts.createDefault();
229
230 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(defaultSslContext,
231 NoopHostnameVerifier.INSTANCE);
232
233 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
234 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
235 Assertions.assertThrows(SSLException.class, () -> {
236 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
237 socket, target.getHostName(), target.getPort(), null, context)) {
238
239 }
240 });
241 }
242 }
243
244 @Test
245 void testSSLTrustVerificationOverrideWithCustom() throws Exception {
246 final TrustStrategy trustStrategy = (chain, authType) -> chain.length == 1;
247 testSSLTrustVerificationOverride(trustStrategy);
248 }
249
250 private void testSSLTrustVerificationOverride(final TrustStrategy trustStrategy)
251 throws Exception {
252
253 this.server = ServerBootstrap.bootstrap()
254 .setSslContext(SSLTestContexts.createServerSSLContext())
255 .setRequestRouter((r, c) -> null)
256 .create();
257
258 this.server.start();
259
260 final HttpClientContext context = HttpClientContext.create();
261
262
263 final SSLContext sslContext = SSLContexts.custom()
264 .loadTrustMaterial(null, trustStrategy)
265 .build();
266
267 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(sslContext,
268 NoopHostnameVerifier.INSTANCE);
269
270 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
271 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
272 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
273 socket,
274 target.getHostName(),
275 target.getPort(),
276 null,
277 context)) {
278
279 }
280 }
281 }
282
283 @Test
284 void testSSLDisabledByDefault() throws Exception {
285
286 this.server = ServerBootstrap.bootstrap()
287 .setSslContext(SSLTestContexts.createServerSSLContext())
288 .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[] {"SSLv3"}))
289 .setRequestRouter((r, c) -> null)
290 .create();
291
292 this.server.start();
293
294 final HttpClientContext context = HttpClientContext.create();
295 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
296 SSLTestContexts.createClientSSLContext());
297 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
298 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
299 Assertions.assertThrows(IOException.class, () ->
300 tlsStrategy.upgrade(
301 socket,
302 target.getHostName(),
303 target.getPort(),
304 null,
305 context));
306 }
307 }
308
309 @Test
310 void testWeakCiphersDisabledByDefault() {
311 final String[] weakCiphersSuites = {
312 "SSL_RSA_WITH_RC4_128_SHA",
313 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
314 "TLS_DH_anon_WITH_AES_128_CBC_SHA",
315 "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
316 "SSL_RSA_WITH_NULL_SHA",
317 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
318 "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
319 "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
320 "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
321 "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
322 "TLS_RSA_WITH_NULL_SHA256",
323 "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
324 "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
325 "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
326 "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"
327 };
328 for (final String cipherSuite : weakCiphersSuites) {
329 final Exception exception = Assertions.assertThrows(Exception.class, () ->
330 testWeakCipherDisabledByDefault(cipherSuite));
331 assertThat(exception, CoreMatchers.anyOf(
332 CoreMatchers.instanceOf(IOException.class),
333 CoreMatchers.instanceOf(IllegalArgumentException.class)));
334 }
335 }
336
337 private void testWeakCipherDisabledByDefault(final String cipherSuite) throws Exception {
338
339 this.server = ServerBootstrap.bootstrap()
340 .setSslContext(SSLTestContexts.createServerSSLContext())
341 .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[] {cipherSuite}))
342 .setRequestRouter((r, c) -> null)
343 .create();
344
345 this.server.start();
346
347 final HttpClientContext context = HttpClientContext.create();
348 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
349 SSLTestContexts.createClientSSLContext());
350 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
351 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
352 tlsStrategy.upgrade(
353 socket,
354 target.getHostName(),
355 target.getPort(),
356 null,
357 context);
358 }
359 }
360
361 @Test
362 void testHostnameVerificationClient() throws Exception {
363
364 this.server = ServerBootstrap.bootstrap()
365 .setSslContext(SSLTestContexts.createServerSSLContext())
366 .setRequestRouter((r, c) -> null)
367 .create();
368
369 this.server.start();
370
371 final HttpHost target1 = new HttpHost("https", "localhost", server.getLocalPort());
372
373 try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
374 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
375 SSLTestContexts.createClientSSLContext(),
376 HostnameVerificationPolicy.CLIENT,
377 HttpsSupport.getDefaultHostnameVerifier());
378 final HttpClientContext context = HttpClientContext.create();
379 final SSLSocket upgradedSocket = tlsStrategy.upgrade(
380 socket,
381 target1.getHostName(),
382 target1.getPort(),
383 null,
384 context);
385 final SSLSession session = upgradedSocket.getSession();
386 MatcherAssert.assertThat(Objects.toString(session.getPeerPrincipal()), Matchers.startsWith("CN=localhost"));
387 }
388
389 final HttpHost target2 = new HttpHost("https", "some-other-host", server.getLocalPort());
390
391 try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
392 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
393 SSLTestContexts.createClientSSLContext(),
394 HostnameVerificationPolicy.CLIENT,
395 HttpsSupport.getDefaultHostnameVerifier());
396 final HttpClientContext context = HttpClientContext.create();
397 Assertions.assertThrows(SSLPeerUnverifiedException.class, () ->
398 tlsStrategy.upgrade(
399 socket,
400 target2.getHostName(),
401 target2.getPort(),
402 null,
403 context));
404 }
405
406 try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
407 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
408 SSLTestContexts.createClientSSLContext(),
409 HostnameVerificationPolicy.CLIENT,
410 NoopHostnameVerifier.INSTANCE);
411 final HttpClientContext context = HttpClientContext.create();
412 final SSLSocket upgradedSocket = tlsStrategy.upgrade(
413 socket,
414 target1.getHostName(),
415 target1.getPort(),
416 null,
417 context);
418 final SSLSession session = upgradedSocket.getSession();
419 MatcherAssert.assertThat(Objects.toString(session.getPeerPrincipal()), Matchers.startsWith("CN=localhost"));
420 }
421 }
422
423 @Test
424 void testHostnameVerificationBuiltIn() throws Exception {
425
426 this.server = ServerBootstrap.bootstrap()
427 .setSslContext(SSLTestContexts.createServerSSLContext())
428 .setRequestRouter((r, c) -> null)
429 .create();
430
431 this.server.start();
432
433 final HttpHost target1 = new HttpHost("https", "localhost", server.getLocalPort());
434
435 try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
436 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
437 SSLTestContexts.createClientSSLContext(),
438 HostnameVerificationPolicy.BUILTIN,
439 NoopHostnameVerifier.INSTANCE);
440 final HttpClientContext context = HttpClientContext.create();
441 final SSLSocket upgradedSocket = tlsStrategy.upgrade(
442 socket,
443 target1.getHostName(),
444 target1.getPort(),
445 null,
446 context);
447 final SSLSession session = upgradedSocket.getSession();
448 MatcherAssert.assertThat(Objects.toString(session.getPeerPrincipal()), Matchers.startsWith("CN=localhost"));
449 }
450
451 final HttpHost target2 = new HttpHost("https", "some-other-host", server.getLocalPort());
452
453 try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
454 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
455 SSLTestContexts.createClientSSLContext(),
456 HostnameVerificationPolicy.BUILTIN,
457 NoopHostnameVerifier.INSTANCE);
458 final HttpClientContext context = HttpClientContext.create();
459 Assertions.assertThrows(SSLHandshakeException.class, () ->
460 tlsStrategy.upgrade(
461 socket,
462 target2.getHostName(),
463 target2.getPort(),
464 null,
465 context));
466 }
467 }
468
469 }