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.http.nio.integration;
29
30 import java.io.IOException;
31 import java.math.BigInteger;
32 import java.net.InetSocketAddress;
33 import java.net.URL;
34 import java.util.concurrent.ExecutionException;
35 import java.util.concurrent.Future;
36 import java.util.concurrent.TimeUnit;
37 import java.util.concurrent.atomic.AtomicReference;
38
39 import javax.net.ssl.SSLContext;
40 import javax.net.ssl.SSLEngine;
41 import javax.net.ssl.SSLException;
42 import javax.net.ssl.SSLHandshakeException;
43 import javax.net.ssl.SSLSession;
44
45 import org.apache.http.HttpException;
46 import org.apache.http.HttpHost;
47 import org.apache.http.HttpRequest;
48 import org.apache.http.HttpResponse;
49 import org.apache.http.config.ConnectionConfig;
50 import org.apache.http.impl.nio.pool.BasicNIOConnFactory;
51 import org.apache.http.message.BasicHttpRequest;
52 import org.apache.http.nio.NHttpConnection;
53 import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
54 import org.apache.http.nio.reactor.IOSession;
55 import org.apache.http.nio.reactor.ListenerEndpoint;
56 import org.apache.http.nio.reactor.ssl.SSLSetupHandler;
57 import org.apache.http.nio.testserver.ClientConnectionFactory;
58 import org.apache.http.nio.testserver.HttpClientNio;
59 import org.apache.http.nio.testserver.HttpServerNio;
60 import org.apache.http.nio.testserver.ServerConnectionFactory;
61 import org.apache.http.nio.util.TestingSupport;
62 import org.apache.http.protocol.HttpContext;
63 import org.apache.http.protocol.HttpCoreContext;
64 import org.apache.http.protocol.HttpRequestHandler;
65 import org.apache.http.ssl.SSLContextBuilder;
66 import org.apache.http.ssl.SSLContexts;
67 import org.hamcrest.CoreMatchers;
68 import org.junit.Assert;
69 import org.junit.Rule;
70 import org.junit.Test;
71 import org.junit.rules.ExternalResource;
72
73 public class TestTLSIntegration {
74
75 private final static long RESULT_TIMEOUT_SEC = 30;
76
77 private static int JRE_LEVEL = TestingSupport.determineJRELevel();
78
79 private HttpServerNio server;
80
81 @Rule
82 public ExternalResource serverResource = new ExternalResource() {
83
84 @Override
85 protected void after() {
86 if (server != null) {
87 try {
88 server.shutdown();
89 } catch (final Exception ignore) {
90 }
91 }
92 }
93
94 };
95
96 private HttpClientNio client;
97
98 @Rule
99 public ExternalResource clientResource = new ExternalResource() {
100
101 @Override
102 protected void after() {
103 if (client != null) {
104 try {
105 client.shutdown();
106 } catch (final Exception ignore) {
107 }
108 }
109 }
110
111 };
112
113 private static SSLContext createServerSSLContext() throws Exception {
114 if (JRE_LEVEL >= 8) {
115 final URL keyStoreURL = TestTLSIntegration.class.getResource("/test-server.p12");
116 final String storePassword = "nopassword";
117 return SSLContextBuilder.create()
118 .setKeyStoreType("pkcs12")
119 .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
120 .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
121 .build();
122 } else {
123 final URL keyStoreURL = TestTLSIntegration.class.getResource("/test.keystore");
124 final String storePassword = "nopassword";
125 return SSLContextBuilder.create()
126 .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
127 .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
128 .build();
129 }
130 }
131
132 private static SSLContext createClientSSLContext() throws Exception {
133 if (JRE_LEVEL >= 8) {
134 final URL keyStoreURL = TestTLSIntegration.class.getResource("/test-client.p12");
135 final String storePassword = "nopassword";
136 return SSLContextBuilder.create()
137 .setKeyStoreType("pkcs12")
138 .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
139 .build();
140 } else {
141 final URL keyStoreURL = TestTLSIntegration.class.getResource("/test.keystore");
142 final String storePassword = "nopassword";
143 return SSLContextBuilder.create()
144 .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
145 .build();
146 }
147 }
148
149 @Test
150 public void testTLSSuccess() throws Exception {
151 server = new HttpServerNio();
152 server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), null));
153 server.setTimeout(5000);
154 server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
155 server.start();
156
157 final AtomicReference<SSLSession> sslSessionRef = new AtomicReference<SSLSession>(null);
158
159 this.client = new HttpClientNio(new BasicNIOConnFactory(createClientSSLContext(), new SSLSetupHandler() {
160
161 @Override
162 public void initalize(final SSLEngine sslEngine) throws SSLException {
163
164 }
165
166 @Override
167 public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
168 sslSessionRef.set(sslSession);
169 }
170
171 }, ConnectionConfig.DEFAULT));
172 client.setTimeout(5000);
173 client.start();
174
175 final ListenerEndpoint endpoint = server.getListenerEndpoint();
176 endpoint.waitFor();
177
178 final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
179
180 final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
181
182 final BasicHttpRequest request = new BasicHttpRequest("GET", "BLAHx200");
183 final Future<HttpResponse> future = client.execute(target, request);
184 final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
185 Assert.assertThat(response, CoreMatchers.notNullValue());
186 Assert.assertThat(response.getStatusLine().getStatusCode(), CoreMatchers.equalTo(200));
187
188 final SSLSession sslSession = sslSessionRef.getAndSet(null);
189 if (JRE_LEVEL >= 8) {
190 Assert.assertThat(sslSession.getPeerPrincipal().getName(),
191 CoreMatchers.equalTo("CN=Test Server,OU=HttpComponents Project,O=Apache Software Foundation"));
192 } else {
193 Assert.assertThat(sslSession.getPeerPrincipal().getName(),
194 CoreMatchers.equalTo("CN=localhost,OU=Apache HttpComponents,O=Apache Software Foundation"));
195 }
196 }
197
198 @Test
199 public void testTLSTrustFailure() throws Exception {
200 server = new HttpServerNio();
201 server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), null));
202 server.setTimeout(5000);
203 server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
204 server.start();
205
206 client = new HttpClientNio(new BasicNIOConnFactory(SSLContexts.createDefault(), null, ConnectionConfig.DEFAULT));
207 client.setTimeout(5000);
208 client.start();
209
210 final ListenerEndpoint endpoint = server.getListenerEndpoint();
211 endpoint.waitFor();
212
213 final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
214
215 final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
216
217 final BasicHttpRequest request = new BasicHttpRequest("GET", "BLAHx200");
218 final Future<HttpResponse> future = client.execute(target, request);
219 try {
220 future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
221 Assert.fail("ExecutionException expected");
222 } catch (final ExecutionException ex) {
223 final Throwable cause = ex.getCause();
224 Assert.assertThat(cause, CoreMatchers.<Throwable>instanceOf(SSLHandshakeException.class));
225 }
226 }
227
228 @Test
229 public void testTLSClientAuthFailure() throws Exception {
230 server = new HttpServerNio();
231 server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), new SSLSetupHandler() {
232
233 @Override
234 public void initalize(final SSLEngine sslEngine) throws SSLException {
235 sslEngine.setNeedClientAuth(true);
236 }
237
238 @Override
239 public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
240 }
241
242 }));
243 server.setTimeout(5000);
244 server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
245 server.start();
246
247 this.client = new HttpClientNio(new BasicNIOConnFactory(createClientSSLContext(), null, ConnectionConfig.DEFAULT));
248 client.setTimeout(5000);
249 client.start();
250
251 final ListenerEndpoint endpoint = server.getListenerEndpoint();
252 endpoint.waitFor();
253
254 final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
255
256 final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
257
258 final BasicHttpRequest request = new BasicHttpRequest("GET", "BLAHx200");
259 final Future<HttpResponse> future = client.execute(target, request);
260 try {
261 future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
262 Assert.fail("ExecutionException expected");
263 } catch (final ExecutionException ex) {
264 final Throwable cause = ex.getCause();
265 Assert.assertThat(cause, CoreMatchers.<Throwable>instanceOf(IOException.class));
266 }
267 }
268
269 @Test
270 public void testTLSProtocolMismatch() throws Exception {
271 server = new HttpServerNio();
272 server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), new SSLSetupHandler() {
273
274 @Override
275 public void initalize(final SSLEngine sslEngine) throws SSLException {
276 sslEngine.setEnabledProtocols(new String[]{"TLSv1.2"});
277 }
278
279 @Override
280 public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
281 }
282
283 }));
284 server.setTimeout(5000);
285 server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
286 server.start();
287
288 this.client = new HttpClientNio(new BasicNIOConnFactory(createClientSSLContext(), new SSLSetupHandler() {
289
290 @Override
291 public void initalize(final SSLEngine sslEngine) throws SSLException {
292 sslEngine.setEnabledProtocols(new String[]{"SSLv3"});
293 }
294
295 @Override
296 public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
297 }
298
299 }, ConnectionConfig.DEFAULT));
300 client.setTimeout(5000);
301 client.start();
302
303 final ListenerEndpoint endpoint = server.getListenerEndpoint();
304 endpoint.waitFor();
305
306 final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
307
308 final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
309
310 final BasicHttpRequest request = new BasicHttpRequest("GET", "BLAHx200");
311 final Future<HttpResponse> future = client.execute(target, request);
312 try {
313 future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
314 Assert.fail("ExecutionException expected");
315 } catch (final ExecutionException ex) {
316 final Throwable cause = ex.getCause();
317 Assert.assertThat(cause, CoreMatchers.<Throwable>instanceOf(IOException.class));
318 }
319 }
320
321 @Test
322 public void testTLSCipherMismatch() throws Exception {
323 server = new HttpServerNio();
324 server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), new SSLSetupHandler() {
325
326 @Override
327 public void initalize(final SSLEngine sslEngine) throws SSLException {
328 sslEngine.setEnabledCipherSuites(new String[]{"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"});
329 }
330
331 @Override
332 public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
333 }
334
335 }));
336 server.setTimeout(5000);
337 server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
338 server.start();
339
340 this.client = new HttpClientNio(new BasicNIOConnFactory(createClientSSLContext(), new SSLSetupHandler() {
341
342 @Override
343 public void initalize(final SSLEngine sslEngine) throws SSLException {
344 sslEngine.setEnabledCipherSuites(new String[]{"SSL_RSA_EXPORT_WITH_RC4_40_MD5"});
345 }
346
347 @Override
348 public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
349 }
350
351 }, ConnectionConfig.DEFAULT));
352 client.setTimeout(5000);
353 client.start();
354
355 final ListenerEndpoint endpoint = server.getListenerEndpoint();
356 endpoint.waitFor();
357
358 final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
359
360 final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
361
362 final BasicHttpRequest request = new BasicHttpRequest("GET", "BLAHx200");
363 final Future<HttpResponse> future = client.execute(target, request);
364 try {
365 future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
366 Assert.fail("ExecutionException expected");
367 } catch (final ExecutionException ex) {
368 final Throwable cause = ex.getCause();
369 Assert.assertThat(cause, CoreMatchers.<Throwable>instanceOf(IOException.class));
370 }
371 }
372
373 @Test
374 public void testCustomSSLContext() throws Exception {
375 final SSLSetupHandler sslSetupHandler = new SSLSetupHandler() {
376
377 @Override
378 public void initalize(
379 final SSLEngine sslengine) throws SSLException {
380 }
381
382 @Override
383 public void verify(
384 final IOSession ioSession, final SSLSession sslsession) throws SSLException {
385 final BigInteger sslid = new BigInteger(sslsession.getId());
386 ioSession.setAttribute("ssl-id", sslid);
387 }
388
389 };
390
391 final HttpRequestHandler requestHandler = new HttpRequestHandler() {
392
393 @Override
394 public void handle(
395 final HttpRequest request,
396 final HttpResponse response,
397 final HttpContext context) throws HttpException, IOException {
398 final NHttpConnection conn = (NHttpConnection) context.getAttribute(
399 HttpCoreContext.HTTP_CONNECTION);
400 final BigInteger sslid = (BigInteger) conn.getContext().getAttribute(
401 "ssl-id");
402 Assert.assertNotNull(sslid);
403 }
404
405 };
406
407 this.server = new HttpServerNio();
408 this.server.setConnectionFactory(new ServerConnectionFactory(createServerSSLContext(), sslSetupHandler));
409 this.server.setTimeout(5000);
410 this.server.registerHandler("*", new BasicAsyncRequestHandler(requestHandler));
411 this.server.start();
412
413 this.client = new HttpClientNio(new BasicNIOConnFactory(new ClientConnectionFactory(createClientSSLContext()), null));
414 this.client.setTimeout(5000);
415 this.client.start();
416
417 final ListenerEndpoint endpoint = this.server.getListenerEndpoint();
418 endpoint.waitFor();
419
420 final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
421
422 final HttpHost target = new HttpHost("localhost", address.getPort());
423 final BasicHttpRequest request = new BasicHttpRequest("GET", "/");
424 final Future<HttpResponse> future = this.client.execute(target, request);
425 final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
426 Assert.assertNotNull(response);
427 Assert.assertEquals(200, response.getStatusLine().getStatusCode());
428 }
429
430 }