/*******************************************************************************
* Copyright (c) 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.team.build.internal.hjplugin.util;
import java.io.IOException;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
public class SSLContextUtil {
private final static Logger LOGGER = Logger.getLogger(SSLContextUtil.class.getName());
public static final String SSL_TLS = "SSL_TLS"; //$NON-NLS-1$
public static final String TLS = "TLS"; //$NON-NLS-1$
public static final String SSL = "SSL"; //$NON-NLS-1$
/**
* Creates an SSL context factory.
* The returned SSLContext will be created so that it is compatible with the
* current security environment. If a FIPS environment is detected then a
* FIPS 140-2 complaint context will be returned.
*
* @return a {@link SSLContext}
*/
public static SSLContext createSSLContext(TrustManager trustManager) {
return createSSLContext(null, trustManager);
}
/**
* Creates an SSL context factory.
* The returned SSLContext will be created so that it is compatible with the
* current security environment. If a FIPS environment is detected then a
* FIPS 140-2 complaint context will be returned.
*
* @return a {@link SSLContext}
*/
public static SSLContext createSSLContext(KeyManager[] keyManagers, TrustManager trustManager) {
String overrideProtocol = System.getProperty("com.ibm.team.repository.transport.client.protocol"); //$NON-NLS-1$
SSLContext context = null;
if (overrideProtocol != null) {
LOGGER.finer("Attempting to create protocol context using system property: " + overrideProtocol); //$NON-NLS-1$
context = createSSLContext(overrideProtocol, keyManagers, trustManager);
}
if (context == null) {
LOGGER.finer("Attempting to create SSL_TLS context"); //$NON-NLS-1$
context = createSSLContext(SSL_TLS, keyManagers, trustManager);
}
if (context == null) {
LOGGER.finer("Unable to create SSL_TLS context, trying TLS"); //$NON-NLS-1$
// When SSL_TLS doesn't work (e.g. under FIPS), try TLS
context = createSSLContext(TLS, keyManagers, trustManager);
}
if (context == null) {
LOGGER.finer("Unable to create TLS context, trying SSL"); //$NON-NLS-1$
// Fall back to just SSL when the above two are not available
context = createSSLContext(SSL, keyManagers, trustManager);
}
if (context == null) {
/* No encryption algorithm worked. Give up. This should never happen
* in any of our supported configurations. */
throw new RuntimeException("No acceptable encryption algorithm found"); //$NON-NLS-1$
}
return context;
}
// Returns null when the given algorithm fails
private static SSLContext createSSLContext(String algorithm, KeyManager[] keyManagers, TrustManager trustManager) {
SSLContext context;
try {
context = SSLContext.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
LOGGER.log(Level.FINER, "No such algorithm creating SSL Context", e); //$NON-NLS-1$
return null;
}
try {
context.init(keyManagers, new TrustManager[] { trustManager }, null);
} catch (KeyManagementException e) {
LOGGER.log(Level.FINER, "Key management issue creating SSL Context", e); //$NON-NLS-1$
return null;
}
/* Create a socket to ensure this algorithm is acceptable. This will
* correctly disallow certain configurations (such as SSL_TLS under FIPS) */
try {
Socket s = context.getSocketFactory().createSocket();
s.close();
} catch (IOException e) {
LOGGER.log(Level.FINER, "error creating socket ensuring algorithm is acceptable", e); //$NON-NLS-1$
return null;
} catch (IllegalArgumentException e) {
LOGGER.log(Level.FINER, "Illegal argument creating socket ensuring algorithm is acceptable", e); //$NON-NLS-1$
return null;
}
return context;
}
}