View Javadoc
1   /*
2     (C) Copyright IBM Corp. 2005, 2013
3   
4     THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE
5     ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE
6     CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
7   
8     You can obtain a current copy of the Eclipse Public License from
9     http://www.opensource.org/licenses/eclipse-1.0.php
10  
11    @author : Roberto Pineiro, IBM, roberto.pineiro@us.ibm.com
12   * @author : Chung-hao Tan, IBM, chungtan@us.ibm.com
13   * 
14   * 
15   * Change History
16   * Flag       Date        Prog         Description
17   *------------------------------------------------------------------------------- 
18   * 1498130    2006-05-31  lupusalex    Selection of xml parser on a per connection basis
19   * 1535756    2006-08-07  lupusalex    Make code warning free
20   * 1558663    2006-09-14  lupusalex    Support custom socket factories in client connections
21   * 1573723    2006-10-09  lupusalex    Selection of JSSE provider via properties file not feasible
22   * 1573723    2006-10-30  lupusalex    rework: Selection of JSSE provider via properties file not feasible
23   * 1565892    2006-11-28  lupusalex    Make SBLIM client JSR48 compliant
24   * 1660743    2007-02-15  lupusalex    SSLContext is static
25   * 1711092    2006-05-02  lupusalex    Some fixes/additions of log&trace messages
26   * 1815707    2007-10-18  ebak         TLS support
27   * 1815707    2007-10-30  ebak         rework: TLS support
28   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
29   * 2204488 	  2008-10-28  raman_arora  Fix code to remove compiler warnings
30   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
31   * 2531371    2009-02-10  raman_arora  Upgrade client to JDK 1.5 (Phase 2)
32   * 3001345    2010-05-18  blaschke-oss File handle leaks in HttpSocketFactory and LogAndTraceBroker
33   * 3027618    2010-07-14  blaschke-oss Close files/readers in finally blocks
34   * 3111718    2010-11-18  blaschke-oss org.sblim.cimclient SSL Code is using the wrong SSL Property
35   * 3536399    2012-08-25  hellerda     Add client/listener peer authentication properties
36   *    2647    2013-07-01  blaschke-oss Add two ssl protocol properties for http server and client
37   */
38  
39  package org.metricshub.wbem.sblim.cimclient.internal.http;
40  
41  /*-
42   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
43   * WBEM Java Client
44   * ჻჻჻჻჻჻
45   * Copyright 2023 - 2025 MetricsHub
46   * ჻჻჻჻჻჻
47   * Licensed under the Apache License, Version 2.0 (the "License");
48   * you may not use this file except in compliance with the License.
49   * You may obtain a copy of the License at
50   *
51   *      http://www.apache.org/licenses/LICENSE-2.0
52   *
53   * Unless required by applicable law or agreed to in writing, software
54   * distributed under the License is distributed on an "AS IS" BASIS,
55   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
56   * See the License for the specific language governing permissions and
57   * limitations under the License.
58   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
59   */
60  
61  import java.io.FileInputStream;
62  import java.io.FileNotFoundException;
63  import java.io.IOException;
64  import java.net.Socket;
65  import java.security.KeyStore;
66  import java.security.KeyStoreException;
67  import java.security.NoSuchAlgorithmException;
68  import java.security.Principal;
69  import java.security.PrivateKey;
70  import java.security.Provider;
71  import java.security.Security;
72  import java.security.UnrecoverableKeyException;
73  import java.security.cert.CertificateException;
74  import java.security.cert.X509Certificate;
75  import java.util.logging.Level;
76  import javax.net.ServerSocketFactory;
77  import javax.net.SocketFactory;
78  import javax.net.ssl.KeyManager;
79  import javax.net.ssl.KeyManagerFactory;
80  import javax.net.ssl.SSLContext;
81  import javax.net.ssl.TrustManager;
82  import javax.net.ssl.TrustManagerFactory;
83  import javax.net.ssl.X509KeyManager;
84  import javax.net.ssl.X509TrustManager;
85  import org.metricshub.wbem.sblim.cimclient.internal.logging.LogAndTraceBroker;
86  import org.metricshub.wbem.sblim.cimclient.internal.logging.Messages;
87  import org.metricshub.wbem.sblim.cimclient.internal.util.WBEMConfiguration;
88  
89  /**
90   * Class HttpSocketFactory manages socket factories
91   *
92   */
93  public class HttpSocketFactory {
94  	private static HttpSocketFactory cInstance = new HttpSocketFactory();
95  
96  	private HttpSocketFactory() {
97  		// empty
98  	}
99  
100 	/**
101 	 * Returns the singleton instance
102 	 *
103 	 * @return The instance
104 	 */
105 	public static HttpSocketFactory getInstance() {
106 		return cInstance;
107 	}
108 
109 	/**
110 	 * Returns a server socket factory
111 	 *
112 	 * @param pContext
113 	 *            The corresponding SSL context or <code>null</code> for
114 	 *            insecure connections
115 	 *
116 	 * @return The factory
117 	 */
118 	public ServerSocketFactory getServerSocketFactory(SSLContext pContext) {
119 		return pContext != null ? pContext.getServerSocketFactory() : ServerSocketFactory.getDefault();
120 	}
121 
122 	/**
123 	 * Returns a client socket factory
124 	 *
125 	 * @param pContext
126 	 *            The SSL context or <code>null</code> for insecure connections
127 	 * @return The factory
128 	 */
129 	public SocketFactory getClientSocketFactory(SSLContext pContext) {
130 		return pContext != null ? pContext.getSocketFactory() : SocketFactory.getDefault();
131 	}
132 
133 	/**
134 	 * Returns a SSLContext for client sockets corresponding to a given set of
135 	 * configuration properties
136 	 *
137 	 * @param pProperties
138 	 *            The configuration to apply
139 	 * @return The SSL context
140 	 */
141 	public SSLContext getClientSSLContext(final WBEMConfiguration pProperties) {
142 		return getSSLContext(pProperties, false);
143 	}
144 
145 	/**
146 	 * Returns a SSLContext for server sockets corresponding to a given set of
147 	 * configuration properties
148 	 *
149 	 * @param pProperties
150 	 *            The configuration to apply
151 	 * @return The SSL context
152 	 */
153 	public SSLContext getServerSSLContext(final WBEMConfiguration pProperties) {
154 		return getSSLContext(pProperties, true);
155 	}
156 
157 	/**
158 	 * Returns a SSLContext corresponding to a given set of configuration
159 	 * properties
160 	 *
161 	 * @param pProperties
162 	 *            The configuration to apply
163 	 * @param pIsServer
164 	 *            <code>true</code> if a server socket context is requested,
165 	 *            <code>false</code> otherwise
166 	 * @return The SSL context
167 	 */
168 	private SSLContext getSSLContext(final WBEMConfiguration pProperties, boolean pIsServer) {
169 		final LogAndTraceBroker logger = LogAndTraceBroker.getBroker();
170 		logger.entry();
171 
172 		final String provider = pIsServer ? pProperties.getSslServerSocketProvider() : pProperties.getSslSocketProvider();
173 		logger.trace(Level.FINER, "Loading JSSE provider:" + provider);
174 
175 		final Provider securityProvider;
176 
177 		try {
178 			Class<?> providerClass = Class.forName(provider);
179 			securityProvider = (java.security.Provider) providerClass.newInstance();
180 			if (Security.getProvider(securityProvider.getName()) == null) {
181 				Security.addProvider(securityProvider);
182 			}
183 		} catch (Exception e) {
184 			logger.trace(Level.FINER, "Exception while loading JSSE provider", e);
185 			logger.message(Messages.SSL_JSSE_PROVIDER_LOAD_FAILED, provider);
186 			logger.exit();
187 			throw new RuntimeException(e);
188 		}
189 
190 		try {
191 			KeyManager[] keyManager = loadKeystore(pProperties, securityProvider, pIsServer);
192 
193 			TrustManager[] trustManager = loadTruststore(pProperties, securityProvider, pIsServer);
194 
195 			String sslProtocol = pIsServer ? pProperties.getSslListenerProtocol() : pProperties.getSslClientProtocol();
196 
197 			SSLContext sslContext = SSLContext.getInstance(
198 				sslProtocol != null ? sslProtocol : pProperties.getSslProtocol(),
199 				securityProvider
200 			);
201 
202 			sslContext.init(keyManager, trustManager, null);
203 
204 			logger.exit();
205 
206 			return sslContext;
207 		} catch (Exception e) {
208 			logger.trace(Level.FINER, "Exception while initializing SSL context (provider:" + provider + ")", e);
209 			logger.message(Messages.SSL_CONTEXT_INIT_FAILED);
210 			logger.exit();
211 			return null;
212 		}
213 	}
214 
215 	private TrustManager[] loadTruststore(
216 		final WBEMConfiguration pProperties,
217 		final Provider pSecurityProvider,
218 		boolean pIsServer
219 	) {
220 		final LogAndTraceBroker logger = LogAndTraceBroker.getBroker();
221 		logger.entry();
222 
223 		final TrustManager[] trustAll = new X509TrustManager[] { new AllTrustManager() };
224 		final TrustManager[] trustNone = new X509TrustManager[] { new NoTrustManager() };
225 		TrustManager[] trustManager = trustNone;
226 
227 		final String truststorePath = pProperties.getSslTrustStorePath();
228 		final char[] truststorePassword = pProperties.getSslTrustStorePassword().toCharArray();
229 		final String truststoreType = pProperties.getSslTrustStoreType();
230 		final String trustManagerAlgorithm = pProperties.getSslTrustManagerAlgorithm();
231 		final boolean clientPeerVerification = pProperties.getSslClientPeerVerification();
232 		final String listenerPeerVerification = pProperties.getSslListenerPeerVerification();
233 
234 		logger.trace(
235 			Level.FINER,
236 			"Using SSL truststore \"" + truststorePath + "\" (" + truststoreType + "/" + trustManagerAlgorithm + ")"
237 		);
238 
239 		if (pIsServer && listenerPeerVerification.equalsIgnoreCase("ignore") || (!pIsServer && !clientPeerVerification)) {
240 			trustManager = trustAll;
241 			if (truststorePath == null || truststorePath.trim().length() == 0) {
242 				logger.trace(Level.FINER, "Peer verification disabled for " + (pIsServer ? "Listener" : "Client"));
243 			} else {
244 				logger.message(Messages.SSL_TRUSTSTORE_INACTIVE);
245 			}
246 		} else {
247 			if (truststorePath == null || truststorePath.trim().length() == 0) {
248 				logger.trace(
249 					Level.FINER,
250 					"Peer verification enabled for " + (pIsServer ? "Listener" : "Client") + " but no truststore specified!"
251 				);
252 				logger.message(Messages.SSL_TRUSTSTORE_NULL);
253 			} else {
254 				logger.trace(Level.FINER, "Peer verification enabled for " + (pIsServer ? "Listener" : "Client"));
255 				FileInputStream fis = null;
256 				try {
257 					final KeyStore trustStore = KeyStore.getInstance(truststoreType);
258 					fis = new FileInputStream(truststorePath);
259 					trustStore.load(fis, truststorePassword);
260 					final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
261 						trustManagerAlgorithm,
262 						pSecurityProvider
263 					);
264 					trustManagerFactory.init(trustStore);
265 					trustManager = trustManagerFactory.getTrustManagers();
266 					logger.trace(Level.FINER, "Truststore successfully loaded for " + (pIsServer ? "Listener" : "Client"));
267 				} catch (FileNotFoundException e) {
268 					logger.trace(Level.FINER, "Exception while loading truststore", e);
269 					logger.message(Messages.SSL_TRUSTSTORE_NOT_FOUND, truststorePath);
270 				} catch (IOException e) {
271 					logger.trace(Level.FINER, "Exception while loading truststore", e);
272 					logger.message(Messages.SSL_TRUSTSTORE_NOT_READABLE, truststorePath);
273 				} catch (NoSuchAlgorithmException e) {
274 					logger.trace(Level.FINER, "Exception while loading truststore", e);
275 					logger.message(Messages.SSL_TRUSTSTORE_INVALID_ALGORITHM, trustManagerAlgorithm);
276 				} catch (CertificateException e) {
277 					logger.trace(Level.FINER, "Exception while loading truststore", e);
278 					logger.message(Messages.SSL_TRUSTSTORE_INVALID_CERT, truststorePath);
279 				} catch (KeyStoreException e) {
280 					logger.trace(Level.FINER, "Exception while loading truststore", e);
281 					logger.message(Messages.SSL_TRUSTSTORE_INVALID, truststoreType);
282 				} catch (Exception e) {
283 					logger.trace(Level.FINER, "Exception while loading truststore", e);
284 					logger.message(Messages.SSL_TRUSTSTORE_OTHER, truststorePath);
285 				} finally {
286 					if (fis != null) {
287 						try {
288 							fis.close();
289 						} catch (IOException e) {
290 							logger.trace(Level.FINER, "Exception while closing truststore", e);
291 						}
292 					}
293 				}
294 			}
295 		}
296 
297 		if (trustManager == trustAll) {
298 			logger.message(Messages.SSL_TRUSTSTORE_FALLBACK);
299 		} else if (trustManager == trustNone) {
300 			logger.message(Messages.SSL_TRUSTSTORE_FALLBACK_NOTRUST);
301 		} else {
302 			logger.message(Messages.SSL_TRUSTSTORE_ACTIVE);
303 		}
304 
305 		logger.exit();
306 		return trustManager;
307 	}
308 
309 	private KeyManager[] loadKeystore(
310 		final WBEMConfiguration pProperties,
311 		final Provider pSecurityProvider,
312 		boolean pIsServer
313 	) {
314 		final LogAndTraceBroker logger = LogAndTraceBroker.getBroker();
315 		logger.entry();
316 
317 		final KeyManager[] noKeys = new KeyManager[] { new EmptyKeyManager() };
318 		KeyManager[] keyManager = noKeys;
319 
320 		final String keystorePath = pProperties.getSslKeyStorePath();
321 		final char[] keystorePassword = pProperties.getSslKeyStorePassword().toCharArray();
322 		final String keystoreType = pProperties.getSslKeyStoreType();
323 		final String keyManagerAlgorithm = pProperties.getSslKeyManagerAlgorithm();
324 
325 		logger.trace(
326 			Level.FINER,
327 			"Using SSL keystore \"" + keystorePath + "\" (" + keystoreType + "/" + keyManagerAlgorithm + ")"
328 		);
329 
330 		if (keystorePath == null || keystorePath.trim().length() == 0) {
331 			logger.trace(Level.FINER, "Keystore not specified for " + (pIsServer ? "Listener" : "Client"));
332 			logger.message(Messages.SSL_KEYSTORE_NULL);
333 		} else {
334 			logger.trace(Level.FINER, "Keystore specified and activated for " + (pIsServer ? "Listener" : "Client"));
335 			FileInputStream fis = null;
336 			try {
337 				final KeyStore keystore = KeyStore.getInstance(keystoreType);
338 				fis = new FileInputStream(keystorePath);
339 				keystore.load(fis, keystorePassword);
340 
341 				final KeyManagerFactory keymanagerfactory = KeyManagerFactory.getInstance(
342 					keyManagerAlgorithm,
343 					pSecurityProvider
344 				);
345 				keymanagerfactory.init(keystore, keystorePassword);
346 				keyManager = keymanagerfactory.getKeyManagers();
347 				logger.trace(Level.FINER, "Keystore successfully loaded for " + (pIsServer ? "Listener" : "Client"));
348 			} catch (FileNotFoundException e) {
349 				logger.trace(Level.FINER, "Exception while loading keystore", e);
350 				logger.message(Messages.SSL_KEYSTORE_NOT_FOUND, keystorePath);
351 			} catch (IOException e) {
352 				logger.trace(Level.FINER, "Exception while loading keystore", e);
353 				logger.message(Messages.SSL_KEYSTORE_NOT_READABLE, keystorePath);
354 			} catch (NoSuchAlgorithmException e) {
355 				logger.trace(Level.FINER, "Exception while loading keystore", e);
356 				logger.message(Messages.SSL_KEYSTORE_INVALID_ALGORITHM, keyManagerAlgorithm);
357 			} catch (CertificateException e) {
358 				logger.trace(Level.FINER, "Exception while loading keystore", e);
359 				logger.message(Messages.SSL_KEYSTORE_INVALID_CERT, keystorePath);
360 			} catch (UnrecoverableKeyException e) {
361 				logger.trace(Level.FINER, "Exception while loading keystore", e);
362 				logger.message(Messages.SSL_KEYSTORE_UNRECOVERABLE_KEY, keystorePath);
363 			} catch (KeyStoreException e) {
364 				logger.trace(Level.FINER, "Exception while loading keystore", e);
365 				logger.message(Messages.SSL_KEYSTORE_INVALID, keystoreType);
366 			} catch (Exception e) {
367 				logger.trace(Level.FINER, "Exception while loading keystore", e);
368 				logger.message(Messages.SSL_KEYSTORE_OTHER, keystorePath);
369 			} finally {
370 				if (fis != null) {
371 					try {
372 						fis.close();
373 					} catch (IOException e) {
374 						logger.trace(Level.FINER, "Exception while closing keystore", e);
375 					}
376 				}
377 			}
378 		}
379 
380 		if (keyManager == noKeys) {
381 			logger.message(Messages.SSL_KEYSTORE_FALLBACK);
382 		}
383 
384 		logger.exit();
385 		return keyManager;
386 	}
387 }
388 
389 class AllTrustManager implements X509TrustManager {
390 
391 	/**
392 	 * @param arg0
393 	 * @param arg1
394 	 */
395 	public void checkClientTrusted(X509Certificate[] arg0, String arg1) {
396 		return;
397 	}
398 
399 	/**
400 	 * @param arg0
401 	 * @param arg1
402 	 */
403 	public void checkServerTrusted(X509Certificate[] arg0, String arg1) {
404 		return;
405 	}
406 
407 	public X509Certificate[] getAcceptedIssuers() {
408 		return null;
409 	}
410 }
411 
412 class NoTrustManager implements X509TrustManager {
413 
414 	/**
415 	 * @param arg0
416 	 * @param arg1
417 	 * @throws CertificateException
418 	 */
419 	public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
420 		throw new CertificateException();
421 	}
422 
423 	/**
424 	 * @param arg0
425 	 * @param arg1
426 	 * @throws CertificateException
427 	 */
428 	public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
429 		throw new CertificateException();
430 	}
431 
432 	public X509Certificate[] getAcceptedIssuers() {
433 		return null;
434 	}
435 }
436 
437 class EmptyKeyManager implements X509KeyManager {
438 
439 	/**
440 	 * @param keyType
441 	 * @param issuers
442 	 * @param socket
443 	 * @return String chooseClientAlias
444 	 */
445 	public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
446 		return null;
447 	}
448 
449 	/**
450 	 * @param keyType
451 	 * @param issuers
452 	 * @param socket
453 	 * @return String chooseServerAlias
454 	 */
455 	public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
456 		return null;
457 	}
458 
459 	/**
460 	 * @param alias
461 	 * @return X509Certificate[]
462 	 */
463 	public X509Certificate[] getCertificateChain(String alias) {
464 		return null;
465 	}
466 
467 	/**
468 	 * @param keyType
469 	 * @param issuers
470 	 * @return String[]
471 	 */
472 	public String[] getClientAliases(String keyType, Principal[] issuers) {
473 		return null;
474 	}
475 
476 	/**
477 	 * @param alias
478 	 * @return PrivateKey
479 	 */
480 	public PrivateKey getPrivateKey(String alias) {
481 		return null;
482 	}
483 
484 	/**
485 	 * @param keyType
486 	 * @param issuers
487 	 * @return String[]
488 	 */
489 	public String[] getServerAliases(String keyType, Principal[] issuers) {
490 		return null;
491 	}
492 }