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
39 import javax.net.ssl.HostnameVerifier;
40 import javax.net.ssl.SSLContext;
41 import javax.net.ssl.SSLException;
42 import javax.net.ssl.SSLSession;
43 import javax.net.ssl.SSLSocket;
44
45 import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
46 import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
47 import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
48 import org.apache.hc.client5.http.ssl.TrustAllStrategy;
49 import org.apache.hc.client5.http.ssl.TrustSelfSignedStrategy;
50 import org.apache.hc.client5.testing.SSLTestContexts;
51 import org.apache.hc.core5.http.HttpHost;
52 import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
53 import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
54 import org.apache.hc.core5.http.protocol.BasicHttpContext;
55 import org.apache.hc.core5.http.protocol.HttpContext;
56 import org.apache.hc.core5.io.CloseMode;
57 import org.apache.hc.core5.ssl.SSLContexts;
58 import org.apache.hc.core5.ssl.TrustStrategy;
59 import org.apache.hc.core5.util.TimeValue;
60 import org.hamcrest.CoreMatchers;
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 public class TestSSLSocketFactory {
69
70 private HttpServer server;
71
72 @AfterEach
73 public void shutDown() throws Exception {
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 public void testBasicSSL() throws Exception {
97
98 this.server = ServerBootstrap.bootstrap()
99 .setSslContext(SSLTestContexts.createServerSSLContext())
100 .create();
101
102 this.server.start();
103
104 final HttpContext context = new BasicHttpContext();
105 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
106 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
107 SSLTestContexts.createClientSSLContext(), hostVerifier);
108 try (final Socket socket = socketFactory.createSocket(context)) {
109 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
110 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
111 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
112 TimeValue.ZERO_MILLISECONDS,
113 socket,
114 target,
115 remoteAddress,
116 null,
117 context)) {
118 final SSLSession sslsession = sslSocket.getSession();
119
120 Assertions.assertNotNull(sslsession);
121 Assertions.assertTrue(hostVerifier.isFired());
122 }
123 }
124 }
125
126 @Test
127 public void testBasicDefaultHostnameVerifier() throws Exception {
128
129 this.server = ServerBootstrap.bootstrap()
130 .setSslContext(SSLTestContexts.createServerSSLContext())
131 .create();
132
133 this.server.start();
134
135 final HttpContext context = new BasicHttpContext();
136 final SSLConnectionSocketFactory socketFactory = SSLConnectionSocketFactoryBuilder.create()
137 .setSslContext(SSLTestContexts.createClientSSLContext())
138 .build();
139 try (final Socket socket = socketFactory.createSocket(context)) {
140 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
141 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
142 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
143 TimeValue.ZERO_MILLISECONDS,
144 socket,
145 target,
146 remoteAddress,
147 null,
148 context)) {
149 final SSLSession sslsession = sslSocket.getSession();
150
151 Assertions.assertNotNull(sslsession);
152 }
153 }
154 }
155
156 @Test
157 public void testClientAuthSSL() throws Exception {
158
159 this.server = ServerBootstrap.bootstrap()
160 .setSslContext(SSLTestContexts.createServerSSLContext())
161 .create();
162
163 this.server.start();
164
165 final HttpContext context = new BasicHttpContext();
166 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
167 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
168 SSLTestContexts.createClientSSLContext(), hostVerifier);
169 try (final Socket socket = socketFactory.createSocket(context)) {
170 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
171 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
172 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
173 TimeValue.ZERO_MILLISECONDS,
174 socket,
175 target,
176 remoteAddress,
177 null,
178 context)) {
179 final SSLSession sslsession = sslSocket.getSession();
180
181 Assertions.assertNotNull(sslsession);
182 Assertions.assertTrue(hostVerifier.isFired());
183 }
184 }
185 }
186
187 @Test
188 public void testClientAuthSSLFailure() throws Exception {
189
190 this.server = ServerBootstrap.bootstrap()
191 .setSslContext(SSLTestContexts.createServerSSLContext())
192 .setSslSetupHandler(sslParameters -> sslParameters.setNeedClientAuth(true))
193 .create();
194
195 this.server.start();
196
197 final HttpContext context = new BasicHttpContext();
198 final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
199 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
200 SSLTestContexts.createClientSSLContext(), hostVerifier);
201 try (final Socket socket = socketFactory.createSocket(context)) {
202 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
203 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
204 Assertions.assertThrows(IOException.class, () -> {
205 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
206 TimeValue.ZERO_MILLISECONDS,
207 socket, target,
208 remoteAddress,
209 null,
210 context)) {
211 final SSLSession sslsession = sslSocket.getSession();
212
213 Assertions.assertNotNull(sslsession);
214 Assertions.assertTrue(hostVerifier.isFired());
215 sslSocket.getInputStream().read();
216 }
217 });
218 }
219 }
220
221 @Test
222 public void testSSLTrustVerification() throws Exception {
223
224 this.server = ServerBootstrap.bootstrap()
225 .setSslContext(SSLTestContexts.createServerSSLContext())
226 .create();
227
228 this.server.start();
229
230 final HttpContext context = new BasicHttpContext();
231
232 final SSLContext defaultsslcontext = SSLContexts.createDefault();
233
234 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(defaultsslcontext,
235 NoopHostnameVerifier.INSTANCE);
236
237 try (final Socket socket = socketFactory.createSocket(context)) {
238 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
239 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
240 Assertions.assertThrows(SSLException.class, () -> {
241 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(
242 TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context)) {
243
244 }
245 });
246 }
247 }
248
249 @Test
250 public void testSSLTrustVerificationOverrideWithCustsom() throws Exception {
251 final TrustStrategy trustStrategy = (chain, authType) -> chain.length == 1;
252 testSSLTrustVerificationOverride(trustStrategy);
253 }
254
255 @Test
256 public void testSSLTrustVerificationOverrideWithTrustSelfSignedStrategy() throws Exception {
257 testSSLTrustVerificationOverride(TrustSelfSignedStrategy.INSTANCE);
258 }
259
260 @Test
261 public void testSSLTrustVerificationOverrideWithTrustAllStrategy() throws Exception {
262 testSSLTrustVerificationOverride(TrustAllStrategy.INSTANCE);
263 }
264
265 private void testSSLTrustVerificationOverride(final TrustStrategy trustStrategy)
266 throws Exception, IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
267
268 this.server = ServerBootstrap.bootstrap()
269 .setSslContext(SSLTestContexts.createServerSSLContext())
270 .create();
271
272 this.server.start();
273
274 final HttpContext context = new BasicHttpContext();
275
276
277 final SSLContext sslcontext = SSLContexts.custom()
278 .loadTrustMaterial(null, trustStrategy)
279 .build();
280
281 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslcontext,
282 NoopHostnameVerifier.INSTANCE);
283
284 try (final Socket socket = socketFactory.createSocket(context)) {
285 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
286 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
287 try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress,
288 null, context)) {
289
290 }
291 }
292 }
293
294 @Test
295 public void testSSLDisabledByDefault() throws Exception {
296
297 this.server = ServerBootstrap.bootstrap()
298 .setSslContext(SSLTestContexts.createServerSSLContext())
299 .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[] {"SSLv3"}))
300 .create();
301
302 this.server.start();
303
304 final HttpContext context = new BasicHttpContext();
305 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
306 SSLTestContexts.createClientSSLContext());
307 try (final Socket socket = socketFactory.createSocket(context)) {
308 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
309 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
310 Assertions.assertThrows(IOException.class, () ->
311 socketFactory.connectSocket(
312 TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context));
313 }
314 }
315
316 @Test
317 public void testWeakCiphersDisabledByDefault() {
318 final String[] weakCiphersSuites = {
319 "SSL_RSA_WITH_RC4_128_SHA",
320 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
321 "TLS_DH_anon_WITH_AES_128_CBC_SHA",
322 "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
323 "SSL_RSA_WITH_NULL_SHA",
324 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
325 "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
326 "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
327 "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
328 "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
329 "TLS_RSA_WITH_NULL_SHA256",
330 "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
331 "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
332 "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
333 "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"
334 };
335 for (final String cipherSuite : weakCiphersSuites) {
336 final Exception exception = Assertions.assertThrows(Exception.class, () ->
337 testWeakCipherDisabledByDefault(cipherSuite));
338 assertThat(exception, CoreMatchers.anyOf(
339 CoreMatchers.instanceOf(IOException.class),
340 CoreMatchers.instanceOf(IllegalArgumentException.class)));
341 }
342 }
343
344 private void testWeakCipherDisabledByDefault(final String cipherSuite) throws Exception {
345
346 this.server = ServerBootstrap.bootstrap()
347 .setSslContext(SSLTestContexts.createServerSSLContext())
348 .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[] {cipherSuite}))
349 .create();
350
351 this.server.start();
352
353 final HttpContext context = new BasicHttpContext();
354 final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
355 SSLTestContexts.createClientSSLContext());
356 try (final Socket socket = socketFactory.createSocket(context)) {
357 final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
358 final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
359 socketFactory.connectSocket(TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context);
360 }
361 }
362 }