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