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 .create();
104
105 this.server.start();
106
107 final HttpClientContext context = HttpClientContext.create();
108 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
109 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
110 SSLTestContexts.createClientSSLContext(), hostVerifier);
111 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
112 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
113 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
114 socket,
115 target.getHostName(),
116 target.getPort(),
117 null,
118 context)) {
119 final SSLSession sslsession = sslSocket.getSession();
120
121 Assertions.assertNotNull(sslsession);
122 Assertions.assertTrue(hostVerifier.isFired());
123 }
124 }
125 }
126
127 @Test
128 public void testBasicDefaultHostnameVerifier() throws Exception {
129
130 this.server = ServerBootstrap.bootstrap()
131 .setSslContext(SSLTestContexts.createServerSSLContext())
132 .create();
133
134 this.server.start();
135
136 final HttpClientContext context = HttpClientContext.create();
137 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext());
138 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
139 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
140 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
141 socket,
142 target.getHostName(),
143 target.getPort(),
144 null,
145 context)) {
146 final SSLSession sslsession = sslSocket.getSession();
147
148 Assertions.assertNotNull(sslsession);
149 }
150 }
151 }
152
153 @Test
154 public void testClientAuthSSL() throws Exception {
155
156 this.server = ServerBootstrap.bootstrap()
157 .setSslContext(SSLTestContexts.createServerSSLContext())
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 public void testClientAuthSSLFailure() throws Exception {
184
185 this.server = ServerBootstrap.bootstrap()
186 .setSslContext(SSLTestContexts.createServerSSLContext())
187 .setSslSetupHandler(sslParameters -> sslParameters.setNeedClientAuth(true))
188 .create();
189
190 this.server.start();
191
192 final HttpClientContext context = HttpClientContext.create();
193 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
194 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
195 SSLTestContexts.createClientSSLContext(), hostVerifier);
196 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
197 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
198 Assertions.assertThrows(IOException.class, () -> {
199 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
200 socket,
201 target.getHostName(),
202 target.getPort(),
203 null,
204 context)) {
205 final SSLSession sslsession = sslSocket.getSession();
206
207 Assertions.assertNotNull(sslsession);
208 Assertions.assertTrue(hostVerifier.isFired());
209 sslSocket.getInputStream().read();
210 }
211 });
212 }
213 }
214
215 @Test
216 public void testSSLTrustVerification() throws Exception {
217
218 this.server = ServerBootstrap.bootstrap()
219 .setSslContext(SSLTestContexts.createServerSSLContext())
220 .create();
221
222 this.server.start();
223
224 final HttpClientContext context = HttpClientContext.create();
225
226 final SSLContext defaultSslContext = SSLContexts.createDefault();
227
228 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(defaultSslContext,
229 NoopHostnameVerifier.INSTANCE);
230
231 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
232 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
233 Assertions.assertThrows(SSLException.class, () -> {
234 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
235 socket, target.getHostName(), target.getPort(), null, context)) {
236
237 }
238 });
239 }
240 }
241
242 @Test
243 public void testSSLTrustVerificationOverrideWithCustom() throws Exception {
244 final TrustStrategy trustStrategy = (chain, authType) -> chain.length == 1;
245 testSSLTrustVerificationOverride(trustStrategy);
246 }
247
248 private void testSSLTrustVerificationOverride(final TrustStrategy trustStrategy)
249 throws Exception, IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
250
251 this.server = ServerBootstrap.bootstrap()
252 .setSslContext(SSLTestContexts.createServerSSLContext())
253 .create();
254
255 this.server.start();
256
257 final HttpClientContext context = HttpClientContext.create();
258
259
260 final SSLContext sslContext = SSLContexts.custom()
261 .loadTrustMaterial(null, trustStrategy)
262 .build();
263
264 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(sslContext,
265 NoopHostnameVerifier.INSTANCE);
266
267 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
268 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
269 try (final SSLSocket sslSocket = tlsStrategy.upgrade(
270 socket,
271 target.getHostName(),
272 target.getPort(),
273 null,
274 context)) {
275
276 }
277 }
278 }
279
280 @Test
281 public void testSSLDisabledByDefault() throws Exception {
282
283 this.server = ServerBootstrap.bootstrap()
284 .setSslContext(SSLTestContexts.createServerSSLContext())
285 .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[] {"SSLv3"}))
286 .create();
287
288 this.server.start();
289
290 final HttpClientContext context = HttpClientContext.create();
291 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
292 SSLTestContexts.createClientSSLContext());
293 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
294 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
295 Assertions.assertThrows(IOException.class, () ->
296 tlsStrategy.upgrade(
297 socket,
298 target.getHostName(),
299 target.getPort(),
300 null,
301 context));
302 }
303 }
304
305 @Test
306 public void testWeakCiphersDisabledByDefault() {
307 final String[] weakCiphersSuites = {
308 "SSL_RSA_WITH_RC4_128_SHA",
309 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
310 "TLS_DH_anon_WITH_AES_128_CBC_SHA",
311 "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
312 "SSL_RSA_WITH_NULL_SHA",
313 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
314 "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
315 "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
316 "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
317 "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
318 "TLS_RSA_WITH_NULL_SHA256",
319 "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
320 "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
321 "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
322 "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"
323 };
324 for (final String cipherSuite : weakCiphersSuites) {
325 final Exception exception = Assertions.assertThrows(Exception.class, () ->
326 testWeakCipherDisabledByDefault(cipherSuite));
327 assertThat(exception, CoreMatchers.anyOf(
328 CoreMatchers.instanceOf(IOException.class),
329 CoreMatchers.instanceOf(IllegalArgumentException.class)));
330 }
331 }
332
333 private void testWeakCipherDisabledByDefault(final String cipherSuite) throws Exception {
334
335 this.server = ServerBootstrap.bootstrap()
336 .setSslContext(SSLTestContexts.createServerSSLContext())
337 .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[] {cipherSuite}))
338 .create();
339
340 this.server.start();
341
342 final HttpClientContext context = HttpClientContext.create();
343 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
344 SSLTestContexts.createClientSSLContext());
345 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
346 try (final Socket socket = new Socket(target.getHostName(), target.getPort())) {
347 tlsStrategy.upgrade(
348 socket,
349 target.getHostName(),
350 target.getPort(),
351 null,
352 context);
353 }
354 }
355
356 @Test
357 public void testHostnameVerificationClient() throws Exception {
358
359 this.server = ServerBootstrap.bootstrap()
360 .setSslContext(SSLTestContexts.createServerSSLContext())
361 .create();
362
363 this.server.start();
364
365 final HttpHost target1 = new HttpHost("https", "localhost", server.getLocalPort());
366
367 try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
368 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
369 SSLTestContexts.createClientSSLContext(),
370 HostnameVerificationPolicy.CLIENT,
371 HttpsSupport.getDefaultHostnameVerifier());
372 final HttpClientContext context = HttpClientContext.create();
373 final SSLSocket upgradedSocket = tlsStrategy.upgrade(
374 socket,
375 target1.getHostName(),
376 target1.getPort(),
377 null,
378 context);
379 final SSLSession session = upgradedSocket.getSession();
380 MatcherAssert.assertThat(Objects.toString(session.getPeerPrincipal()), Matchers.startsWith("CN=localhost"));
381 }
382
383 final HttpHost target2 = new HttpHost("https", "some-other-host", server.getLocalPort());
384
385 try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
386 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
387 SSLTestContexts.createClientSSLContext(),
388 HostnameVerificationPolicy.CLIENT,
389 HttpsSupport.getDefaultHostnameVerifier());
390 final HttpClientContext context = HttpClientContext.create();
391 Assertions.assertThrows(SSLPeerUnverifiedException.class, () ->
392 tlsStrategy.upgrade(
393 socket,
394 target2.getHostName(),
395 target2.getPort(),
396 null,
397 context));
398 }
399
400 try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
401 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
402 SSLTestContexts.createClientSSLContext(),
403 HostnameVerificationPolicy.CLIENT,
404 NoopHostnameVerifier.INSTANCE);
405 final HttpClientContext context = HttpClientContext.create();
406 final SSLSocket upgradedSocket = tlsStrategy.upgrade(
407 socket,
408 target1.getHostName(),
409 target1.getPort(),
410 null,
411 context);
412 final SSLSession session = upgradedSocket.getSession();
413 MatcherAssert.assertThat(Objects.toString(session.getPeerPrincipal()), Matchers.startsWith("CN=localhost"));
414 }
415 }
416
417 @Test
418 public void testHostnameVerificationBuiltIn() throws Exception {
419
420 this.server = ServerBootstrap.bootstrap()
421 .setSslContext(SSLTestContexts.createServerSSLContext())
422 .create();
423
424 this.server.start();
425
426 final HttpHost target1 = new HttpHost("https", "localhost", server.getLocalPort());
427
428 try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
429 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
430 SSLTestContexts.createClientSSLContext(),
431 HostnameVerificationPolicy.BUILTIN,
432 NoopHostnameVerifier.INSTANCE);
433 final HttpClientContext context = HttpClientContext.create();
434 final SSLSocket upgradedSocket = tlsStrategy.upgrade(
435 socket,
436 target1.getHostName(),
437 target1.getPort(),
438 null,
439 context);
440 final SSLSession session = upgradedSocket.getSession();
441 MatcherAssert.assertThat(Objects.toString(session.getPeerPrincipal()), Matchers.startsWith("CN=localhost"));
442 }
443
444 final HttpHost target2 = new HttpHost("https", "some-other-host", server.getLocalPort());
445
446 try (final Socket socket = new Socket(InetAddress.getLocalHost(), server.getLocalPort())) {
447 final TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(
448 SSLTestContexts.createClientSSLContext(),
449 HostnameVerificationPolicy.BUILTIN,
450 NoopHostnameVerifier.INSTANCE);
451 final HttpClientContext context = HttpClientContext.create();
452 Assertions.assertThrows(SSLHandshakeException.class, () ->
453 tlsStrategy.upgrade(
454 socket,
455 target2.getHostName(),
456 target2.getPort(),
457 null,
458 context));
459 }
460 }
461
462 }