001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2016 ForgeRock AS. 016 */ 017package org.opends.server.tools; 018 019 020import java.io.FileInputStream; 021import java.io.IOException; 022import java.net.InetAddress; 023import java.net.Socket; 024import java.security.KeyStore; 025import java.security.KeyStoreException; 026import java.security.NoSuchAlgorithmException; 027import java.security.Provider; 028import java.util.Arrays; 029import java.util.List; 030 031import javax.net.ssl.KeyManager; 032import javax.net.ssl.KeyManagerFactory; 033import javax.net.ssl.SSLContext; 034import javax.net.ssl.SSLSocket; 035import javax.net.ssl.SSLSocketFactory; 036import javax.net.ssl.TrustManager; 037import javax.net.ssl.TrustManagerFactory; 038import javax.net.ssl.X509TrustManager; 039 040import org.opends.server.extensions.BlindTrustManagerProvider; 041import org.forgerock.i18n.slf4j.LocalizedLogger; 042import org.forgerock.opendj.ldap.SSLContextBuilder; 043import org.opends.server.util.CollectionUtils; 044import org.opends.server.util.ExpirationCheckTrustManager; 045import org.opends.server.util.SelectableCertificateKeyManager; 046 047import com.forgerock.opendj.cli.ConnectionFactoryProvider; 048 049import static org.opends.messages.ToolMessages.*; 050 051 052/** 053 * This class provides SSL connection related utility functions. 054 */ 055public class SSLConnectionFactory 056{ 057 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 058 059 /** 060 * List of available TLS protocols. By default, corresponds to all TLS protocols available in the JVM. 061 * The list may be overridden if <em>org.opends.ldaps.protocols</em> system property is set. 062 */ 063 private static final String[] TLS_PROTOCOLS; 064 065 static 066 { 067 List<String> protocols = null; 068 try 069 { 070 protocols = ConnectionFactoryProvider.getDefaultProtocols(); 071 } 072 catch (NoSuchAlgorithmException ex) 073 { 074 logger.trace("Unable to retrieve default TLS protocols of the JVM, defaulting to TLSv1", ex); 075 protocols = Arrays.asList(SSLContextBuilder.PROTOCOL_TLS1); 076 } 077 TLS_PROTOCOLS = protocols.toArray(new String[protocols.size()]); 078 } 079 080 private SSLSocketFactory sslSocketFactory; 081 082 /** 083 * Constructor for the SSL connection factory. 084 */ 085 public SSLConnectionFactory() 086 { 087 } 088 089 /** 090 * Initialize the connection factory by creating the key and 091 * trust managers for the SSL connection. 092 * 093 * @param trustAll Indicates whether to blindly trust all 094 * certificates. 095 * @param keyStorePath The path to the key store file. 096 * @param keyStorePassword The PIN to use to access the key store 097 * contents. 098 * @param clientAlias The alias to use for the client certificate. 099 * @param trustStorePath The path to the trust store file. 100 * @param trustStorePassword The PIN to use to access the trust store 101 * contents. 102 * 103 * @throws SSLConnectionException If a problem occurs while initializing the 104 * connection factory. 105 */ 106 public void init(boolean trustAll, String keyStorePath, 107 String keyStorePassword, String clientAlias, 108 String trustStorePath, String trustStorePassword) 109 throws SSLConnectionException 110 { 111 try 112 { 113 SSLContext ctx = SSLContext.getInstance("TLS"); 114 KeyManager[] keyManagers = null; 115 TrustManager[] trustManagers = null; 116 117 if(trustAll) 118 { 119 BlindTrustManagerProvider blindTrustProvider = 120 new BlindTrustManagerProvider(); 121 trustManagers = blindTrustProvider.getTrustManagers(); 122 } else if (trustStorePath == null) { 123 trustManagers = PromptTrustManager.getTrustManagers(); 124 } else 125 { 126 TrustManager[] tmpTrustManagers = 127 getTrustManagers(KeyStore.getDefaultType(), null, trustStorePath, 128 trustStorePassword); 129 trustManagers = new TrustManager[tmpTrustManagers.length]; 130 for (int i=0; i < trustManagers.length; i++) 131 { 132 trustManagers[i] = 133 new ExpirationCheckTrustManager((X509TrustManager) 134 tmpTrustManagers[i]); 135 } 136 } 137 if(keyStorePath != null) 138 { 139 keyManagers = getKeyManagers(KeyStore.getDefaultType(), null, 140 keyStorePath, keyStorePassword); 141 142 if (clientAlias != null) 143 { 144 keyManagers = SelectableCertificateKeyManager.wrap(keyManagers, CollectionUtils.newTreeSet(clientAlias)); 145 } 146 } 147 148 ctx.init(keyManagers, trustManagers, new java.security.SecureRandom()); 149 sslSocketFactory = ctx.getSocketFactory(); 150 } catch(Exception e) 151 { 152 throw new SSLConnectionException( 153 ERR_TOOLS_CANNOT_CREATE_SSL_CONNECTION.get(e.getMessage()), e); 154 } 155 } 156 157 /** 158 * Create the SSL socket connection to the specified host. 159 * 160 * @param hostName The address of the system to which the connection 161 * should be established. 162 * @param portNumber The port number to which the connection should be 163 * established. 164 * 165 * @return The SSL socket established to the specified host. 166 * 167 * @throws SSLConnectionException If a problem occurs while performing SSL 168 * negotiation. 169 * 170 * @throws IOException If a problem occurs while attempting to communicate 171 * with the server. 172 */ 173 public Socket createSocket(String hostName, int portNumber) 174 throws SSLConnectionException, IOException 175 { 176 if(sslSocketFactory == null) 177 { 178 throw new SSLConnectionException(ERR_TOOLS_SSL_CONNECTION_NOT_INITIALIZED.get()); 179 } 180 return socketWithEnabledProtocols(sslSocketFactory.createSocket(hostName, portNumber)); 181 } 182 183 private Socket socketWithEnabledProtocols(Socket socket) 184 { 185 SSLSocket sslSocket = (SSLSocket) socket; 186 sslSocket.setEnabledProtocols(TLS_PROTOCOLS); 187 return sslSocket; 188 } 189 190 /** 191 * Create the SSL socket connection to the specified host. 192 * 193 * @param host 194 * The address of the system to which the connection should be 195 * established. 196 * @param portNumber 197 * The port number to which the connection should be established. 198 * @return The SSL socket established to the specified host. 199 * @throws SSLConnectionException 200 * If a problem occurs while performing SSL negotiation. 201 * @throws IOException 202 * If a problem occurs while attempting to communicate with the 203 * server. 204 */ 205 public Socket createSocket(InetAddress host, int portNumber) 206 throws SSLConnectionException, IOException 207 { 208 if (sslSocketFactory == null) 209 { 210 throw new SSLConnectionException(ERR_TOOLS_SSL_CONNECTION_NOT_INITIALIZED.get()); 211 } 212 return socketWithEnabledProtocols(sslSocketFactory.createSocket(host, portNumber)); 213 } 214 215 /** 216 * Create the SSL socket connection to the specified host layered over 217 * an existing socket. 218 * 219 * @param s The socket to use for the existing connection. 220 * @param hostName The address of the system to which the connection 221 * should be established. 222 * @param portNumber The port number to which the connection should be 223 * established. 224 * @param autoClose Indicates whether the underlying connection should be 225 * automatically closed when the SSL session is ended. 226 * 227 * @return The SSL socket established to the specified host. 228 * 229 * @throws SSLConnectionException If a problem occurs while performing SSL 230 * negotiation. 231 * 232 * @throws IOException If a problem occurs while attempting to communicate 233 * with the server. 234 */ 235 public Socket createSocket(Socket s, String hostName, int portNumber, 236 boolean autoClose) 237 throws SSLConnectionException, IOException 238 { 239 if(sslSocketFactory == null) 240 { 241 throw new SSLConnectionException(ERR_TOOLS_SSL_CONNECTION_NOT_INITIALIZED.get()); 242 } 243 return socketWithEnabledProtocols(sslSocketFactory.createSocket(s, hostName, portNumber, autoClose)); 244 } 245 246 /** 247 * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for 248 * interactions requiring access to a key manager. 249 * 250 * @param keyStoreType The key store type to use with the specified file. 251 * @param provider The provider to use when accessing the key store. 252 * @param keyStoreFile The path to the file containing the key store data. 253 * @param keyStorePass The PIN needed to access the key store contents. 254 * 255 * @return A set of <CODE>KeyManager</CODE> objects that may be used for 256 * interactions requiring access to a key manager. 257 * 258 * @throws KeyStoreException If a problem occurs while interacting with the 259 * key store. 260 * 261 * @throws SSLConnectionException If a problem occurs while trying to load 262 * key store file. 263 */ 264 265 private KeyManager[] getKeyManagers(String keyStoreType, 266 Provider provider, 267 String keyStoreFile, 268 String keyStorePass) 269 throws KeyStoreException, SSLConnectionException 270 { 271 if(keyStoreFile == null) 272 { 273 // Lookup the file name through the JDK property. 274 keyStoreFile = getKeyStore(); 275 } 276 277 if(keyStorePass == null) 278 { 279 // Lookup the keystore PIN through the JDK property. 280 keyStorePass = getKeyStorePIN(); 281 } 282 283 KeyStore ks = null; 284 if(provider != null) 285 { 286 ks = KeyStore.getInstance(keyStoreType, provider); 287 } else 288 { 289 ks = KeyStore.getInstance(keyStoreType); 290 } 291 292 char[] keyStorePIN = null; 293 if(keyStorePass != null) 294 { 295 keyStorePIN = keyStorePass.toCharArray(); 296 } 297 298 try 299 { 300 FileInputStream inputStream = new FileInputStream(keyStoreFile); 301 ks.load(inputStream, keyStorePIN); 302 inputStream.close(); 303 304 } catch(Exception e) 305 { 306 logger.traceException(e); 307 308 throw new SSLConnectionException( 309 ERR_TOOLS_CANNOT_LOAD_KEYSTORE_FILE.get(keyStoreFile), e); 310 } 311 312 try 313 { 314 String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); 315 KeyManagerFactory keyManagerFactory = 316 KeyManagerFactory.getInstance(keyManagerAlgorithm); 317 318 keyManagerFactory.init(ks, keyStorePIN); 319 return keyManagerFactory.getKeyManagers(); 320 } catch(Exception ke) 321 { 322 logger.traceException(ke); 323 324 throw new SSLConnectionException( 325 ERR_TOOLS_CANNOT_INIT_KEYMANAGER.get(keyStoreFile), ke); 326 } 327 328 } 329 330 331 /** 332 * Retrieves a set of <CODE>TrustManager</CODE> objects that may be used for 333 * interactions requiring access to a trust manager. 334 * 335 * @param trustStoreType The trust store type to use with the specified 336 * file. 337 * @param provider The provider to use when accessing the trust store. 338 * @param trustStoreFile The path to the file containing the trust store 339 * data. 340 * @param trustStorePass The PIN needed to access the trust store contents. 341 * 342 * @return A set of <CODE>TrustManager</CODE> objects that may be used for 343 * interactions requiring access to a trust manager. 344 * 345 * @throws KeyStoreException If a problem occurs while interacting with the 346 * trust store. 347 * 348 * @throws SSLConnectionException If a problem occurs while trying to load 349 * trust store file. 350 */ 351 private TrustManager[] getTrustManagers(String trustStoreType, 352 Provider provider, 353 String trustStoreFile, 354 String trustStorePass) 355 throws KeyStoreException, SSLConnectionException 356 { 357 if(trustStoreFile == null) 358 { 359 trustStoreFile = getTrustStore(); 360 // No trust store file available. 361 if(trustStoreFile == null) 362 { 363 return null; 364 } 365 } 366 367 if(trustStorePass == null) 368 { 369 trustStorePass = getTrustStorePIN(); 370 } 371 372 KeyStore trustStore = null; 373 if(provider != null) 374 { 375 trustStore = KeyStore.getInstance(trustStoreType, provider); 376 } else 377 { 378 trustStore = KeyStore.getInstance(trustStoreType); 379 } 380 381 char[] trustStorePIN = null; 382 if(trustStorePass != null) 383 { 384 trustStorePIN = trustStorePass.toCharArray(); 385 } 386 387 try 388 { 389 FileInputStream inputStream = new FileInputStream(trustStoreFile); 390 trustStore.load(inputStream, trustStorePIN); 391 inputStream.close(); 392 } catch(Exception e) 393 { 394 logger.traceException(e); 395 396 throw new SSLConnectionException( 397 ERR_TOOLS_CANNOT_LOAD_TRUSTSTORE_FILE.get(trustStoreFile), e); 398 } 399 400 try 401 { 402 String trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); 403 TrustManagerFactory trustManagerFactory = 404 TrustManagerFactory.getInstance(trustManagerAlgorithm); 405 406 trustManagerFactory.init(trustStore); 407 return trustManagerFactory.getTrustManagers(); 408 } catch(Exception ke) 409 { 410 logger.traceException(ke); 411 412 throw new SSLConnectionException( 413 ERR_TOOLS_CANNOT_INIT_TRUSTMANAGER.get(trustStoreFile), ke); 414 } 415 416 } 417 418 /** 419 * Read the KeyStore PIN from the JSSE system property. 420 * 421 * @return The PIN that should be used to access the key store. 422 */ 423 424 private String getKeyStorePIN() 425 { 426 return System.getProperty("javax.net.ssl.keyStorePassword"); 427 } 428 429 /** 430 * Read the TrustStore PIN from the JSSE system property. 431 * 432 * @return The PIN that should be used to access the trust store. 433 */ 434 435 private String getTrustStorePIN() 436 { 437 return System.getProperty("javax.net.ssl.trustStorePassword"); 438 } 439 440 /** 441 * Read the KeyStore from the JSSE system property. 442 * 443 * @return The path to the key store file. 444 */ 445 446 private String getKeyStore() 447 { 448 return System.getProperty("javax.net.ssl.keyStore"); 449 } 450 451 /** 452 * Read the TrustStore from the JSSE system property. 453 * 454 * @return The path to the trust store file. 455 */ 456 457 private String getTrustStore() 458 { 459 return System.getProperty("javax.net.ssl.trustStore"); 460 } 461 462} 463