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.InetSocketAddress;
34 import java.net.Socket;
35 import java.security.KeyManagementException;
36 import java.security.KeyStoreException;
37 import java.security.NoSuchAlgorithmException;
38 import java.util.concurrent.atomic.AtomicBoolean;
39
40 import javax.net.ssl.HostnameVerifier;
41 import javax.net.ssl.SSLContext;
42 import javax.net.ssl.SSLException;
43 import javax.net.ssl.SSLSession;
44 import javax.net.ssl.SSLSocket;
45
46 import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
47 import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
48 import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
49 import org.apache.hc.client5.http.ssl.TrustAllStrategy;
50 import org.apache.hc.client5.http.ssl.TrustSelfSignedStrategy;
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.http.protocol.BasicHttpContext;
56 import org.apache.hc.core5.http.protocol.HttpContext;
57 import org.apache.hc.core5.io.CloseMode;
58 import org.apache.hc.core5.ssl.SSLContexts;
59 import org.apache.hc.core5.ssl.TrustStrategy;
60 import org.apache.hc.core5.util.TimeValue;
61 import org.apache.hc.core5.util.Timeout;
62 import org.hamcrest.CoreMatchers;
63 import org.junit.jupiter.api.AfterEach;
64 import org.junit.jupiter.api.Assertions;
65 import org.junit.jupiter.api.Test;
66
67
68
69
70 public class TestSSLSocketFactory {
71
72 private HttpServer server;
73
74 @AfterEach
75 public void shutDown() throws Exception {
76 if (this.server != null) {
77 this.server.close(CloseMode.GRACEFUL);
78 }
79 }
80
81 static class TestX509HostnameVerifier implements HostnameVerifier {
82
83 private boolean fired;
84
85 @Override
86 public boolean verify(final String host, final SSLSession session) {
87 this.fired = true;
88 return true;
89 }
90
91 public boolean isFired() {
92 return this.fired;
93 }
94
95 }
96
97 @Test
98 public void testBasicSSL() throws Exception {
99
100 this.server = ServerBootstrap.bootstrap()
101 .setSslContext(SSLTestContexts.createServerSSLContext())
102 .create();
103
104 this.server.start();
105
106 final HttpContext context = new BasicHttpContext();
107 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
108 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
109 SSLTestContexts.createClientSSLContext(), hostVerifier);
110 try (final Socket socket = socketFactory.createSocket(context)) {
111 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
112 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
113 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
114 TimeValue.ZERO_MILLISECONDS,
115 socket,
116 target,
117 remoteAddress,
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 testBasicSslConnectOverride() throws Exception {
130 this.server = ServerBootstrap.bootstrap()
131 .setSslContext(SSLTestContexts.createServerSSLContext())
132 .create();
133 this.server.start();
134
135 final HttpContext context = new BasicHttpContext();
136 final AtomicBoolean connectCalled = new AtomicBoolean();
137 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
138 SSLTestContexts.createClientSSLContext()) {
139 @Override
140 protected void connectSocket(
141 final Socket sock,
142 final InetSocketAddress remoteAddress,
143 final Timeout connectTimeout,
144 final HttpContext context) throws IOException {
145 connectCalled.set(true);
146 super.connectSocket(sock, remoteAddress, connectTimeout, context);
147 }
148 };
149 try (final Socket socket = socketFactory.createSocket(context)) {
150 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
151 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
152 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
153 TimeValue.ZERO_MILLISECONDS,
154 socket,
155 target,
156 remoteAddress,
157 null,
158 context)) {
159 final SSLSession sslsession = sslSocket.getSession();
160 Assertions.assertNotNull(sslsession);
161 Assertions.assertTrue(connectCalled.get());
162 }
163 }
164 }
165
166 @Test
167 public void testBasicDefaultHostnameVerifier() throws Exception {
168
169 this.server = ServerBootstrap.bootstrap()
170 .setSslContext(SSLTestContexts.createServerSSLContext())
171 .create();
172
173 this.server.start();
174
175 final HttpContext context = new BasicHttpContext();
176 final SSLConnectionSocketFactory socketFactory = SSLConnectionSocketFactoryBuilder.create()
177 .setSslContext(SSLTestContexts.createClientSSLContext())
178 .build();
179 try (final Socket socket = socketFactory.createSocket(context)) {
180 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
181 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
182 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
183 TimeValue.ZERO_MILLISECONDS,
184 socket,
185 target,
186 remoteAddress,
187 null,
188 context)) {
189 final SSLSession sslsession = sslSocket.getSession();
190
191 Assertions.assertNotNull(sslsession);
192 }
193 }
194 }
195
196 @Test
197 public void testClientAuthSSL() throws Exception {
198
199 this.server = ServerBootstrap.bootstrap()
200 .setSslContext(SSLTestContexts.createServerSSLContext())
201 .create();
202
203 this.server.start();
204
205 final HttpContext context = new BasicHttpContext();
206 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
207 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
208 SSLTestContexts.createClientSSLContext(), hostVerifier);
209 try (final Socket socket = socketFactory.createSocket(context)) {
210 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
211 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
212 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
213 TimeValue.ZERO_MILLISECONDS,
214 socket,
215 target,
216 remoteAddress,
217 null,
218 context)) {
219 final SSLSession sslsession = sslSocket.getSession();
220
221 Assertions.assertNotNull(sslsession);
222 Assertions.assertTrue(hostVerifier.isFired());
223 }
224 }
225 }
226
227 @Test
228 public void testClientAuthSSLFailure() throws Exception {
229
230 this.server = ServerBootstrap.bootstrap()
231 .setSslContext(SSLTestContexts.createServerSSLContext())
232 .setSslSetupHandler(sslParameters -> sslParameters.setNeedClientAuth(true))
233 .create();
234
235 this.server.start();
236
237 final HttpContext context = new BasicHttpContext();
238 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
239 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
240 SSLTestContexts.createClientSSLContext(), hostVerifier);
241 try (final Socket socket = socketFactory.createSocket(context)) {
242 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
243 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
244 Assertions.assertThrows(IOException.class, () -> {
245 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
246 TimeValue.ZERO_MILLISECONDS,
247 socket, target,
248 remoteAddress,
249 null,
250 context)) {
251 final SSLSession sslsession = sslSocket.getSession();
252
253 Assertions.assertNotNull(sslsession);
254 Assertions.assertTrue(hostVerifier.isFired());
255 sslSocket.getInputStream().read();
256 }
257 });
258 }
259 }
260
261 @Test
262 public void testSSLTrustVerification() throws Exception {
263
264 this.server = ServerBootstrap.bootstrap()
265 .setSslContext(SSLTestContexts.createServerSSLContext())
266 .create();
267
268 this.server.start();
269
270 final HttpContext context = new BasicHttpContext();
271
272 final SSLContext defaultSslContext = SSLContexts.createDefault();
273
274 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(defaultSslContext,
275 NoopHostnameVerifier.INSTANCE);
276
277 try (final Socket socket = socketFactory.createSocket(context)) {
278 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
279 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
280 Assertions.assertThrows(SSLException.class, () -> {
281 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
282 TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context)) {
283
284 }
285 });
286 }
287 }
288
289 @Test
290 public void testSSLTrustVerificationOverrideWithCustsom() throws Exception {
291 final TrustStrategy trustStrategy = (chain, authType) -> chain.length == 1;
292 testSSLTrustVerificationOverride(trustStrategy);
293 }
294
295 @Test
296 public void testSSLTrustVerificationOverrideWithTrustSelfSignedStrategy() throws Exception {
297 testSSLTrustVerificationOverride(TrustSelfSignedStrategy.INSTANCE);
298 }
299
300 @Test
301 public void testSSLTrustVerificationOverrideWithTrustAllStrategy() throws Exception {
302 testSSLTrustVerificationOverride(TrustAllStrategy.INSTANCE);
303 }
304
305 private void testSSLTrustVerificationOverride(final TrustStrategy trustStrategy)
306 throws Exception, IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
307
308 this.server = ServerBootstrap.bootstrap()
309 .setSslContext(SSLTestContexts.createServerSSLContext())
310 .create();
311
312 this.server.start();
313
314 final HttpContext context = new BasicHttpContext();
315
316
317 final SSLContext sslContext = SSLContexts.custom()
318 .loadTrustMaterial(null, trustStrategy)
319 .build();
320
321 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext,
322 NoopHostnameVerifier.INSTANCE);
323
324 try (final Socket socket = socketFactory.createSocket(context)) {
325 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
326 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
327 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress,
328 null, context)) {
329
330 }
331 }
332 }
333
334 @Test
335 public void testSSLDisabledByDefault() throws Exception {
336
337 this.server = ServerBootstrap.bootstrap()
338 .setSslContext(SSLTestContexts.createServerSSLContext())
339 .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[] {"SSLv3"}))
340 .create();
341
342 this.server.start();
343
344 final HttpContext context = new BasicHttpContext();
345 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
346 SSLTestContexts.createClientSSLContext());
347 try (final Socket socket = socketFactory.createSocket(context)) {
348 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
349 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
350 Assertions.assertThrows(IOException.class, () ->
351 socketFactory.connectSocket(
352 TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context));
353 }
354 }
355
356 @Test
357 public void testWeakCiphersDisabledByDefault() {
358 final String[] weakCiphersSuites = {
359 "SSL_RSA_WITH_RC4_128_SHA",
360 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
361 "TLS_DH_anon_WITH_AES_128_CBC_SHA",
362 "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
363 "SSL_RSA_WITH_NULL_SHA",
364 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
365 "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
366 "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
367 "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
368 "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
369 "TLS_RSA_WITH_NULL_SHA256",
370 "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
371 "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
372 "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
373 "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"
374 };
375 for (final String cipherSuite : weakCiphersSuites) {
376 final Exception exception = Assertions.assertThrows(Exception.class, () ->
377 testWeakCipherDisabledByDefault(cipherSuite));
378 assertThat(exception, CoreMatchers.anyOf(
379 CoreMatchers.instanceOf(IOException.class),
380 CoreMatchers.instanceOf(IllegalArgumentException.class)));
381 }
382 }
383
384 private void testWeakCipherDisabledByDefault(final String cipherSuite) throws Exception {
385
386 this.server = ServerBootstrap.bootstrap()
387 .setSslContext(SSLTestContexts.createServerSSLContext())
388 .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[] {cipherSuite}))
389 .create();
390
391 this.server.start();
392
393 final HttpContext context = new BasicHttpContext();
394 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
395 SSLTestContexts.createClientSSLContext());
396 try (final Socket socket = socketFactory.createSocket(context)) {
397 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
398 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
399 socketFactory.connectSocket(TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context);
400 }
401 }
402 }