]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | ||
20 | package org.apache.thrift.transport; | |
21 | ||
22 | import java.io.FileInputStream; | |
23 | import java.io.FileNotFoundException; | |
24 | import java.io.InputStream; | |
25 | import java.io.IOException; | |
26 | import java.net.InetAddress; | |
27 | import java.net.URL; | |
28 | import java.net.MalformedURLException; | |
29 | import java.security.KeyStore; | |
30 | import java.util.Arrays; | |
31 | ||
32 | import javax.net.ssl.KeyManagerFactory; | |
33 | import javax.net.ssl.SSLContext; | |
34 | import javax.net.ssl.SSLServerSocket; | |
35 | import javax.net.ssl.SSLServerSocketFactory; | |
36 | import javax.net.ssl.SSLSocket; | |
37 | import javax.net.ssl.SSLSocketFactory; | |
38 | import javax.net.ssl.TrustManagerFactory; | |
39 | ||
40 | import org.slf4j.Logger; | |
41 | import org.slf4j.LoggerFactory; | |
42 | ||
43 | /** | |
44 | * A Factory for providing and setting up Client and Server SSL wrapped | |
45 | * TSocket and TServerSocket | |
46 | */ | |
47 | public class TSSLTransportFactory { | |
48 | ||
49 | private static final Logger LOGGER = | |
50 | LoggerFactory.getLogger(TSSLTransportFactory.class); | |
51 | ||
52 | /** | |
53 | * Get a SSL wrapped TServerSocket bound to the specified port. In this | |
54 | * configuration the default settings are used. Default settings are retrieved | |
55 | * from System properties that are set. | |
56 | * | |
57 | * Example system properties: | |
58 | * -Djavax.net.ssl.trustStore=<truststore location> | |
59 | * -Djavax.net.ssl.trustStorePassword=password | |
60 | * -Djavax.net.ssl.keyStore=<keystore location> | |
61 | * -Djavax.net.ssl.keyStorePassword=password | |
62 | * | |
63 | * @param port | |
64 | * @return A SSL wrapped TServerSocket | |
65 | * @throws TTransportException | |
66 | */ | |
67 | public static TServerSocket getServerSocket(int port) throws TTransportException { | |
68 | return getServerSocket(port, 0); | |
69 | } | |
70 | ||
71 | /** | |
72 | * Get a default SSL wrapped TServerSocket bound to the specified port | |
73 | * | |
74 | * @param port | |
75 | * @param clientTimeout | |
76 | * @return A SSL wrapped TServerSocket | |
77 | * @throws TTransportException | |
78 | */ | |
79 | public static TServerSocket getServerSocket(int port, int clientTimeout) throws TTransportException { | |
80 | return getServerSocket(port, clientTimeout, false, null); | |
81 | } | |
82 | ||
83 | /** | |
84 | * Get a default SSL wrapped TServerSocket bound to the specified port and interface | |
85 | * | |
86 | * @param port | |
87 | * @param clientTimeout | |
88 | * @param ifAddress | |
89 | * @return A SSL wrapped TServerSocket | |
90 | * @throws TTransportException | |
91 | */ | |
92 | public static TServerSocket getServerSocket(int port, int clientTimeout, boolean clientAuth, InetAddress ifAddress) throws TTransportException { | |
93 | SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); | |
94 | return createServer(factory, port, clientTimeout, clientAuth, ifAddress, null); | |
95 | } | |
96 | ||
97 | /** | |
98 | * Get a configured SSL wrapped TServerSocket bound to the specified port and interface. | |
99 | * Here the TSSLTransportParameters are used to set the values for the algorithms, keystore, | |
100 | * truststore and other settings | |
101 | * | |
102 | * @param port | |
103 | * @param clientTimeout | |
104 | * @param ifAddress | |
105 | * @param params | |
106 | * @return A SSL wrapped TServerSocket | |
107 | * @throws TTransportException | |
108 | */ | |
109 | public static TServerSocket getServerSocket(int port, int clientTimeout, InetAddress ifAddress, TSSLTransportParameters params) throws TTransportException { | |
110 | if (params == null || !(params.isKeyStoreSet || params.isTrustStoreSet)) { | |
111 | throw new TTransportException("Either one of the KeyStore or TrustStore must be set for SSLTransportParameters"); | |
112 | } | |
113 | ||
114 | SSLContext ctx = createSSLContext(params); | |
115 | return createServer(ctx.getServerSocketFactory(), port, clientTimeout, params.clientAuth, ifAddress, params); | |
116 | } | |
117 | ||
118 | private static TServerSocket createServer(SSLServerSocketFactory factory, int port, int timeout, boolean clientAuth, | |
119 | InetAddress ifAddress, TSSLTransportParameters params) throws TTransportException { | |
120 | try { | |
121 | SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(port, 100, ifAddress); | |
122 | serverSocket.setSoTimeout(timeout); | |
123 | serverSocket.setNeedClientAuth(clientAuth); | |
124 | if (params != null && params.cipherSuites != null) { | |
125 | serverSocket.setEnabledCipherSuites(params.cipherSuites); | |
126 | } | |
127 | return new TServerSocket(new TServerSocket.ServerSocketTransportArgs(). | |
128 | serverSocket(serverSocket).clientTimeout(timeout)); | |
129 | } catch (Exception e) { | |
130 | throw new TTransportException("Could not bind to port " + port, e); | |
131 | } | |
132 | } | |
133 | ||
134 | /** | |
135 | * Get a default SSL wrapped TSocket connected to the specified host and port. All | |
136 | * the client methods return a bound connection. So there is no need to call open() on the | |
137 | * TTransport. | |
138 | * | |
139 | * @param host | |
140 | * @param port | |
141 | * @param timeout | |
142 | * @return A SSL wrapped TSocket | |
143 | * @throws TTransportException | |
144 | */ | |
145 | public static TSocket getClientSocket(String host, int port, int timeout) throws TTransportException { | |
146 | SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); | |
147 | return createClient(factory, host, port, timeout); | |
148 | } | |
149 | ||
150 | /** | |
151 | * Get a default SSL wrapped TSocket connected to the specified host and port. | |
152 | * | |
153 | * @param host | |
154 | * @param port | |
155 | * @return A SSL wrapped TSocket | |
156 | * @throws TTransportException | |
157 | */ | |
158 | public static TSocket getClientSocket(String host, int port) throws TTransportException { | |
159 | return getClientSocket(host, port, 0); | |
160 | } | |
161 | ||
162 | /** | |
163 | * Get a custom configured SSL wrapped TSocket. The SSL settings are obtained from the | |
164 | * passed in TSSLTransportParameters. | |
165 | * | |
166 | * @param host | |
167 | * @param port | |
168 | * @param timeout | |
169 | * @param params | |
170 | * @return A SSL wrapped TSocket | |
171 | * @throws TTransportException | |
172 | */ | |
173 | public static TSocket getClientSocket(String host, int port, int timeout, TSSLTransportParameters params) throws TTransportException { | |
174 | if (params == null || !(params.isKeyStoreSet || params.isTrustStoreSet)) { | |
175 | throw new TTransportException("Either one of the KeyStore or TrustStore must be set for SSLTransportParameters"); | |
176 | } | |
177 | ||
178 | SSLContext ctx = createSSLContext(params); | |
179 | return createClient(ctx.getSocketFactory(), host, port, timeout); | |
180 | } | |
181 | ||
182 | private static SSLContext createSSLContext(TSSLTransportParameters params) throws TTransportException { | |
183 | SSLContext ctx; | |
184 | InputStream in = null; | |
185 | InputStream is = null; | |
186 | ||
187 | try { | |
188 | ctx = SSLContext.getInstance(params.protocol); | |
189 | TrustManagerFactory tmf = null; | |
190 | KeyManagerFactory kmf = null; | |
191 | ||
192 | if (params.isTrustStoreSet) { | |
193 | tmf = TrustManagerFactory.getInstance(params.trustManagerType); | |
194 | KeyStore ts = KeyStore.getInstance(params.trustStoreType); | |
195 | if (params.trustStoreStream != null) { | |
196 | in = params.trustStoreStream; | |
197 | } else { | |
198 | in = getStoreAsStream(params.trustStore); | |
199 | } | |
200 | ts.load(in, | |
201 | (params.trustPass != null ? params.trustPass.toCharArray() : null)); | |
202 | tmf.init(ts); | |
203 | } | |
204 | ||
205 | if (params.isKeyStoreSet) { | |
206 | kmf = KeyManagerFactory.getInstance(params.keyManagerType); | |
207 | KeyStore ks = KeyStore.getInstance(params.keyStoreType); | |
208 | if (params.keyStoreStream != null) { | |
209 | is = params.keyStoreStream; | |
210 | } else { | |
211 | is = getStoreAsStream(params.keyStore); | |
212 | } | |
213 | ks.load(is, params.keyPass.toCharArray()); | |
214 | kmf.init(ks, params.keyPass.toCharArray()); | |
215 | } | |
216 | ||
217 | if (params.isKeyStoreSet && params.isTrustStoreSet) { | |
218 | ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); | |
219 | } | |
220 | else if (params.isKeyStoreSet) { | |
221 | ctx.init(kmf.getKeyManagers(), null, null); | |
222 | } | |
223 | else { | |
224 | ctx.init(null, tmf.getTrustManagers(), null); | |
225 | } | |
226 | ||
227 | } catch (Exception e) { | |
228 | throw new TTransportException("Error creating the transport", e); | |
229 | } finally { | |
230 | if (in != null) { | |
231 | try { | |
232 | in.close(); | |
233 | } catch (IOException e) { | |
234 | LOGGER.warn("Unable to close stream", e); | |
235 | } | |
236 | } | |
237 | if (is != null) { | |
238 | try { | |
239 | is.close(); | |
240 | } catch (IOException e) { | |
241 | LOGGER.warn("Unable to close stream", e); | |
242 | } | |
243 | } | |
244 | } | |
245 | ||
246 | return ctx; | |
247 | } | |
248 | ||
249 | private static InputStream getStoreAsStream(String store) throws IOException { | |
250 | try { | |
251 | return new FileInputStream(store); | |
252 | } catch(FileNotFoundException e) { | |
253 | } | |
254 | ||
255 | InputStream storeStream = null; | |
256 | try { | |
257 | storeStream = new URL(store).openStream(); | |
258 | if (storeStream != null) { | |
259 | return storeStream; | |
260 | } | |
261 | } catch(MalformedURLException e) { | |
262 | } | |
263 | ||
264 | storeStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(store); | |
265 | ||
266 | if (storeStream != null) { | |
267 | return storeStream; | |
268 | } else { | |
269 | throw new IOException("Could not load file: " + store); | |
270 | } | |
271 | } | |
272 | ||
273 | private static TSocket createClient(SSLSocketFactory factory, String host, int port, int timeout) throws TTransportException { | |
274 | try { | |
275 | SSLSocket socket = (SSLSocket) factory.createSocket(host, port); | |
276 | socket.setSoTimeout(timeout); | |
277 | return new TSocket(socket); | |
278 | } catch (Exception e) { | |
279 | throw new TTransportException("Could not connect to " + host + " on port " + port, e); | |
280 | } | |
281 | } | |
282 | ||
283 | ||
284 | /** | |
285 | * A Class to hold all the SSL parameters | |
286 | */ | |
287 | public static class TSSLTransportParameters { | |
288 | protected String protocol = "TLS"; | |
289 | protected String keyStore; | |
290 | protected InputStream keyStoreStream; | |
291 | protected String keyPass; | |
292 | protected String keyManagerType = KeyManagerFactory.getDefaultAlgorithm(); | |
293 | protected String keyStoreType = "JKS"; | |
294 | protected String trustStore; | |
295 | protected InputStream trustStoreStream; | |
296 | protected String trustPass; | |
297 | protected String trustManagerType = TrustManagerFactory.getDefaultAlgorithm(); | |
298 | protected String trustStoreType = "JKS"; | |
299 | protected String[] cipherSuites; | |
300 | protected boolean clientAuth = false; | |
301 | protected boolean isKeyStoreSet = false; | |
302 | protected boolean isTrustStoreSet = false; | |
303 | ||
304 | public TSSLTransportParameters() {} | |
305 | ||
306 | /** | |
307 | * Create parameters specifying the protocol and cipher suites | |
308 | * | |
309 | * @param protocol The specific protocol (TLS/SSL) can be specified with versions | |
310 | * @param cipherSuites | |
311 | */ | |
312 | public TSSLTransportParameters(String protocol, String[] cipherSuites) { | |
313 | this(protocol, cipherSuites, false); | |
314 | } | |
315 | ||
316 | /** | |
317 | * Create parameters specifying the protocol, cipher suites and if client authentication | |
318 | * is required | |
319 | * | |
320 | * @param protocol The specific protocol (TLS/SSL) can be specified with versions | |
321 | * @param cipherSuites | |
322 | * @param clientAuth | |
323 | */ | |
324 | public TSSLTransportParameters(String protocol, String[] cipherSuites, boolean clientAuth) { | |
325 | if (protocol != null) { | |
326 | this.protocol = protocol; | |
327 | } | |
328 | this.cipherSuites = cipherSuites != null ? Arrays.copyOf(cipherSuites, cipherSuites.length) : null; | |
329 | this.clientAuth = clientAuth; | |
330 | } | |
331 | ||
332 | /** | |
333 | * Set the keystore, password, certificate type and the store type | |
334 | * | |
335 | * @param keyStore Location of the Keystore on disk | |
336 | * @param keyPass Keystore password | |
337 | * @param keyManagerType The default is X509 | |
338 | * @param keyStoreType The default is JKS | |
339 | */ | |
340 | public void setKeyStore(String keyStore, String keyPass, String keyManagerType, String keyStoreType) { | |
341 | this.keyStore = keyStore; | |
342 | this.keyPass = keyPass; | |
343 | if (keyManagerType != null) { | |
344 | this.keyManagerType = keyManagerType; | |
345 | } | |
346 | if (keyStoreType != null) { | |
347 | this.keyStoreType = keyStoreType; | |
348 | } | |
349 | isKeyStoreSet = true; | |
350 | } | |
351 | ||
352 | /** | |
353 | * Set the keystore, password, certificate type and the store type | |
354 | * | |
355 | * @param keyStoreStream Keystore content input stream | |
356 | * @param keyPass Keystore password | |
357 | * @param keyManagerType The default is X509 | |
358 | * @param keyStoreType The default is JKS | |
359 | */ | |
360 | public void setKeyStore(InputStream keyStoreStream, String keyPass, String keyManagerType, String keyStoreType) { | |
361 | this.keyStoreStream = keyStoreStream; | |
362 | setKeyStore("", keyPass, keyManagerType, keyStoreType); | |
363 | } | |
364 | ||
365 | /** | |
366 | * Set the keystore and password | |
367 | * | |
368 | * @param keyStore Location of the Keystore on disk | |
369 | * @param keyPass Keystore password | |
370 | */ | |
371 | public void setKeyStore(String keyStore, String keyPass) { | |
372 | setKeyStore(keyStore, keyPass, null, null); | |
373 | } | |
374 | ||
375 | /** | |
376 | * Set the keystore and password | |
377 | * | |
378 | * @param keyStoreStream Keystore content input stream | |
379 | * @param keyPass Keystore password | |
380 | */ | |
381 | public void setKeyStore(InputStream keyStoreStream, String keyPass) { | |
382 | setKeyStore(keyStoreStream, keyPass, null, null); | |
383 | } | |
384 | ||
385 | /** | |
386 | * Set the truststore, password, certificate type and the store type | |
387 | * | |
388 | * @param trustStore Location of the Truststore on disk | |
389 | * @param trustPass Truststore password | |
390 | * @param trustManagerType The default is X509 | |
391 | * @param trustStoreType The default is JKS | |
392 | */ | |
393 | public void setTrustStore(String trustStore, String trustPass, String trustManagerType, String trustStoreType) { | |
394 | this.trustStore = trustStore; | |
395 | this.trustPass = trustPass; | |
396 | if (trustManagerType != null) { | |
397 | this.trustManagerType = trustManagerType; | |
398 | } | |
399 | if (trustStoreType != null) { | |
400 | this.trustStoreType = trustStoreType; | |
401 | } | |
402 | isTrustStoreSet = true; | |
403 | } | |
404 | ||
405 | /** | |
406 | * Set the truststore, password, certificate type and the store type | |
407 | * | |
408 | * @param trustStoreStream Truststore content input stream | |
409 | * @param trustPass Truststore password | |
410 | * @param trustManagerType The default is X509 | |
411 | * @param trustStoreType The default is JKS | |
412 | */ | |
413 | public void setTrustStore(InputStream trustStoreStream, String trustPass, String trustManagerType, String trustStoreType) { | |
414 | this.trustStoreStream = trustStoreStream; | |
415 | setTrustStore("", trustPass, trustManagerType, trustStoreType); | |
416 | } | |
417 | ||
418 | /** | |
419 | * Set the truststore and password | |
420 | * | |
421 | * @param trustStore Location of the Truststore on disk | |
422 | * @param trustPass Truststore password | |
423 | */ | |
424 | public void setTrustStore(String trustStore, String trustPass) { | |
425 | setTrustStore(trustStore, trustPass, null, null); | |
426 | } | |
427 | ||
428 | /** | |
429 | * Set the truststore and password | |
430 | * | |
431 | * @param trustStoreStream Truststore content input stream | |
432 | * @param trustPass Truststore password | |
433 | */ | |
434 | public void setTrustStore(InputStream trustStoreStream, String trustPass) { | |
435 | setTrustStore(trustStoreStream, trustPass, null, null); | |
436 | } | |
437 | ||
438 | /** | |
439 | * Set if client authentication is required | |
440 | * | |
441 | * @param clientAuth | |
442 | */ | |
443 | public void requireClientAuth(boolean clientAuth) { | |
444 | this.clientAuth = clientAuth; | |
445 | } | |
446 | } | |
447 | } |