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