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.core5.testing.classic;
29
30 import java.io.IOException;
31 import java.util.concurrent.atomic.AtomicReference;
32
33 import javax.net.ssl.SSLException;
34 import javax.net.ssl.SSLHandshakeException;
35 import javax.net.ssl.SSLParameters;
36 import javax.net.ssl.SSLSession;
37
38 import org.apache.hc.core5.function.Callback;
39 import org.apache.hc.core5.http.ClassicHttpRequest;
40 import org.apache.hc.core5.http.ClassicHttpResponse;
41 import org.apache.hc.core5.http.ContentType;
42 import org.apache.hc.core5.http.HttpHost;
43 import org.apache.hc.core5.http.HttpStatus;
44 import org.apache.hc.core5.http.Method;
45 import org.apache.hc.core5.http.ProtocolVersion;
46 import org.apache.hc.core5.http.impl.bootstrap.HttpRequester;
47 import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
48 import org.apache.hc.core5.http.impl.bootstrap.RequesterBootstrap;
49 import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
50 import org.apache.hc.core5.http.io.SocketConfig;
51 import org.apache.hc.core5.http.io.entity.EntityUtils;
52 import org.apache.hc.core5.http.io.entity.StringEntity;
53 import org.apache.hc.core5.http.io.ssl.SSLSessionVerifier;
54 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
55 import org.apache.hc.core5.http.protocol.BasicHttpContext;
56 import org.apache.hc.core5.http.protocol.HttpContext;
57 import org.apache.hc.core5.http.ssl.TLS;
58 import org.apache.hc.core5.io.CloseMode;
59 import org.apache.hc.core5.ssl.SSLContexts;
60 import org.apache.hc.core5.testing.SSLTestContexts;
61 import org.apache.hc.core5.util.Timeout;
62 import org.hamcrest.CoreMatchers;
63 import org.hamcrest.MatcherAssert;
64 import org.junit.Assert;
65 import org.junit.Rule;
66 import org.junit.Test;
67 import org.junit.rules.ExternalResource;
68
69 public class ClassicTLSIntegrationTest {
70
71 private static final Timeout TIMEOUT = Timeout.ofSeconds(30);
72
73 private HttpServer server;
74
75 @Rule
76 public ExternalResource serverResource = new ExternalResource() {
77
78 @Override
79 protected void after() {
80 if (server != null) {
81 try {
82 server.close(CloseMode.IMMEDIATE);
83 } catch (final Exception ignore) {
84 }
85 }
86 }
87
88 };
89
90 private HttpRequester requester;
91
92 @Rule
93 public ExternalResource clientResource = new ExternalResource() {
94
95 @Override
96 protected void after() {
97 if (requester != null) {
98 try {
99 requester.close(CloseMode.GRACEFUL);
100 } catch (final Exception ignore) {
101 }
102 }
103 }
104
105 };
106
107 @Test
108 public void testTLSSuccess() throws Exception {
109 server = ServerBootstrap.bootstrap()
110 .setSocketConfig(SocketConfig.custom()
111 .setSoTimeout(TIMEOUT)
112 .build())
113 .setSslContext(SSLTestContexts.createServerSSLContext())
114 .setExceptionListener(LoggingExceptionListener.INSTANCE)
115 .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
116 .register("*", new EchoHandler())
117 .create();
118 server.start();
119
120 final AtomicReference<SSLSession> sslSessionRef = new AtomicReference<>();
121
122 requester = RequesterBootstrap.bootstrap()
123 .setSslContext(SSLTestContexts.createClientSSLContext())
124 .setSslSessionVerifier(new SSLSessionVerifier() {
125
126 @Override
127 public void verify(final HttpHost endpoint, final SSLSession sslSession) throws SSLException {
128 sslSessionRef.set(sslSession);
129 }
130
131 })
132 .setSocketConfig(SocketConfig.custom()
133 .setSoTimeout(TIMEOUT)
134 .build())
135 .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
136 .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
137 .create();
138
139 final HttpContext context = new BasicHttpContext();
140 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
141 final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff");
142 request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN));
143 try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) {
144 MatcherAssert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
145 final String body1 = EntityUtils.toString(response1.getEntity());
146 MatcherAssert.assertThat(body1, CoreMatchers.equalTo("some stuff"));
147 }
148
149 final SSLSession sslSession = sslSessionRef.getAndSet(null);
150 final ProtocolVersion tlsVersion = TLS.parse(sslSession.getProtocol());
151 MatcherAssert.assertThat(tlsVersion.greaterEquals(TLS.V_1_2.version), CoreMatchers.equalTo(true));
152 MatcherAssert.assertThat(sslSession.getPeerPrincipal().getName(),
153 CoreMatchers.equalTo("CN=localhost,OU=Apache HttpComponents,O=Apache Software Foundation"));
154 }
155
156 @Test(expected = SSLHandshakeException.class)
157 public void testTLSTrustFailure() throws Exception {
158 server = ServerBootstrap.bootstrap()
159 .setSocketConfig(SocketConfig.custom()
160 .setSoTimeout(TIMEOUT)
161 .build())
162 .setSslContext(SSLTestContexts.createServerSSLContext())
163 .setExceptionListener(LoggingExceptionListener.INSTANCE)
164 .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
165 .register("*", new EchoHandler())
166 .create();
167 server.start();
168
169 requester = RequesterBootstrap.bootstrap()
170 .setSslContext(SSLContexts.createDefault())
171 .setSocketConfig(SocketConfig.custom()
172 .setSoTimeout(TIMEOUT)
173 .build())
174 .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
175 .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
176 .create();
177
178 final HttpContext context = new BasicHttpContext();
179 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
180 final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff");
181 request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN));
182 try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) {
183 EntityUtils.consume(response1.getEntity());
184 }
185 }
186
187 @Test(expected = IOException.class)
188 public void testTLSClientAuthFailure() throws Exception {
189 server = ServerBootstrap.bootstrap()
190 .setSslContext(SSLTestContexts.createClientSSLContext())
191 .setSocketConfig(SocketConfig.custom()
192 .setSoTimeout(TIMEOUT)
193 .build())
194 .setSslContext(SSLTestContexts.createServerSSLContext())
195 .setSslSetupHandler(new Callback<SSLParameters>() {
196
197 @Override
198 public void execute(final SSLParameters sslParameters) {
199 sslParameters.setNeedClientAuth(true);
200 }
201
202 })
203 .setExceptionListener(LoggingExceptionListener.INSTANCE)
204 .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
205 .register("*", new EchoHandler())
206 .create();
207 server.start();
208
209 requester = RequesterBootstrap.bootstrap()
210 .setSslContext(SSLTestContexts.createClientSSLContext())
211 .setSocketConfig(SocketConfig.custom()
212 .setSoTimeout(TIMEOUT)
213 .build())
214 .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
215 .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
216 .create();
217
218 final HttpContext context = new BasicHttpContext();
219 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
220 final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff");
221 request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN));
222 try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) {
223 EntityUtils.consume(response1.getEntity());
224 }
225 }
226
227 @Test(expected = IOException.class)
228 public void testSSLDisabledByDefault() throws Exception {
229 server = ServerBootstrap.bootstrap()
230 .setSslContext(SSLTestContexts.createServerSSLContext())
231 .setSslSetupHandler(new Callback<SSLParameters>() {
232
233 @Override
234 public void execute(final SSLParameters sslParameters) {
235 sslParameters.setProtocols(new String[]{"SSLv3"});
236 }
237
238 })
239 .create();
240 server.start();
241
242 requester = RequesterBootstrap.bootstrap()
243 .setSslContext(SSLTestContexts.createClientSSLContext())
244 .setSocketConfig(SocketConfig.custom()
245 .setSoTimeout(TIMEOUT)
246 .build())
247 .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
248 .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
249 .create();
250
251 final HttpContext context = new BasicHttpContext();
252 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
253 final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff");
254 request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN));
255 try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) {
256 EntityUtils.consume(response1.getEntity());
257 }
258 }
259
260 @Test
261 public void testWeakCiphersDisabledByDefault() throws Exception {
262
263 requester = RequesterBootstrap.bootstrap()
264 .setSslContext(SSLTestContexts.createClientSSLContext())
265 .setSocketConfig(SocketConfig.custom()
266 .setSoTimeout(TIMEOUT)
267 .build())
268 .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
269 .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
270 .create();
271
272 final String[] weakCiphersSuites = {
273 "SSL_RSA_WITH_RC4_128_SHA",
274 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
275 "TLS_DH_anon_WITH_AES_128_CBC_SHA",
276 "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
277 "SSL_RSA_WITH_NULL_SHA",
278 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
279 "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
280 "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
281 "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
282 "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
283 "TLS_RSA_WITH_NULL_SHA256",
284 "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
285 "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
286 "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
287 "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"
288 };
289
290 for (final String cipherSuite : weakCiphersSuites) {
291 server = ServerBootstrap.bootstrap()
292 .setSslContext(SSLTestContexts.createServerSSLContext())
293 .setSslSetupHandler(new Callback<SSLParameters>() {
294
295 @Override
296 public void execute(final SSLParameters sslParameters) {
297 sslParameters.setProtocols(new String[]{cipherSuite});
298 }
299
300 })
301 .create();
302 try {
303 server.start();
304
305 final HttpContext context = new BasicHttpContext();
306 final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort());
307 final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff");
308 request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN));
309 try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) {
310 EntityUtils.consume(response1.getEntity());
311 }
312
313 Assert.fail("IOException expected");
314 } catch (final IOException | IllegalArgumentException expected) {
315 } finally {
316 server.close(CloseMode.IMMEDIATE);
317 }
318 }
319 }
320
321 }