All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vmware.cxfrestclient.CxfClientSecurityContext Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
/* *****************************************************************************
 * api-extension-template-vcloud-director
 * Copyright 2018 VMware, Inc.
 * SPDX-License-Identifier: BSD-2-Clause
 * ****************************************************************************/

package com.vmware.cxfrestclient;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import javax.crypto.Cipher;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;

/**
 * A security context for {@link AbstractCxfRestClient} to control select SSL connection and https verification parameters
 * 

* An object of this class can be created using one of the public factory methods: *

    *
  • {@link #getCxfClientSecurityContext(KeyStore, char[], KeyStore, Collection, boolean)}
  • *
  • {@link #getDefaultCxfClientSecurityContext()}
  • *
* * {@code AbstractCxfRestClient} uses an object of this class to determine: *
    *
  • Overriding {@link KeyManager}s, if any
  • *
  • Overriding {@link TrustManager}s, if any
  • *
  • Overriding {@link Collection} of {@code Protocol}s, if any
  • *
  • Overriding {@link Collection} of {@link Cipher}s, if any
  • *
  • Whether hostname must be verified when initiating an HTTPS session. If yes, hostname will be verified using platform {@link HostnameVerifier}
  • *
* */ public final class CxfClientSecurityContext { // 'null' causes JRE's default trust manager/key manager/protocols/cipher suites to be used. private static final X509TrustManager[] DEFAULT_TRUST_MANAGER = null; private static final X509KeyManager[] DEFAULT_KEY_MANAGER = null; public static final Collection DEFAULT_PROTOCOL_LIST = null; public static final Collection DEFAULT_CIPHER_LIST = null; private final boolean enableHostnameVerification; private final SSLSocketFactory sslSocketFactory; private CxfClientSecurityContext() { sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault(); this.enableHostnameVerification = true; } private CxfClientSecurityContext(final KeyManager[] keyManagers, final TrustManager[] trustManagers, final Collection protocols, final Collection ciphers, final boolean enableHostnameVerification) throws GeneralSecurityException { this.enableHostnameVerification = enableHostnameVerification; this.sslSocketFactory = createRestrictedSocketFactory(keyManagers, trustManagers, protocols, ciphers); } private CxfClientSecurityContext(final SSLSocketFactory sslSocketFactory, final boolean enableHostnameVerification) { this.enableHostnameVerification = enableHostnameVerification; this.sslSocketFactory = sslSocketFactory; } /** * Factory method to get a context that will configure {@link AbstractCxfRestClient} to use * system default key managers, trust managers and cipher suites for SSL Handshake and system * default hostname verifier for https */ public static CxfClientSecurityContext getDefaultCxfClientSecurityContext() { return new CxfClientSecurityContext(); } /** * Factory method to get a context that will configure {@link AbstractCxfRestClient}'s SSL * Handshake context and hostname verifier as specified by the input provided * * @param keystore * {@link KeyStore} that contains key material to configure client's * {@link KeyManager}. null input means use system default key manager * @param keyPassword * Password to decrypt information in provided {@code keystore}. Ignored if no * keystore provided * @param truststore * {@link KeyStore} that contains trust material to configure client's * {@link TrustManager}. null means use system default trust manager * @param ciphers * List of ciphers that are permitted for use by the client during SSL Handshake. * {@link #DEFAULT_CIPHER_LIST} means use system default cipher suite * @param enableHostnameVerification * true would indicate using system default hostname verifier for https. * false indicates do not use an all-permitting hostname verifier * @return {@link CxfClientSecurityContext} that will help {@link AbstractCxfRestClient} to pick * up configuration as described. * @throws GeneralSecurityException * if either the keystore or the truststore could not be used to set up necessary * managers */ public static CxfClientSecurityContext getCxfClientSecurityContext(final KeyStore keystore, final char[] keyPassword, final KeyStore truststore, final Collection ciphers, final boolean enableHostnameVerification) throws GeneralSecurityException { return getCxfClientSecurityContext(keystore, keyPassword, truststore, DEFAULT_PROTOCOL_LIST, ciphers, enableHostnameVerification); } /** * Factory method to get a context that will configure {@link AbstractCxfRestClient}'s SSL * Handshake context and hostname verifier as specified by the input provided * * @param keystore * {@link KeyStore} that contains key material to configure client's * {@link KeyManager}. null input means use system default key manager * @param keyPassword * Password to decrypt information in provided {@code keystore}. Ignored if no * keystore provided * @param truststore * {@link KeyStore} that contains trust material to configure client's * {@link TrustManager}. null means use system default trust manager * @param protocols * List of protocols that are permitted for use by the client during SSL Handshake. * {@link #DEFAULT_PROTOCOL_LIST} means use system default cipher suite * @param ciphers * List of ciphers that are permitted for use by the client during SSL Handshake. * {@link #DEFAULT_CIPHER_LIST} means use system default cipher suite * @param enableHostnameVerification * true would indicate using system default hostname verifier for https. * false indicates do not use an all-permitting hostname verifier * @return {@link CxfClientSecurityContext} that will help {@link AbstractCxfRestClient} to pick * up configuration as described. * @throws GeneralSecurityException * if either the keystore or the truststore could not be used to set up necessary * managers */ public static CxfClientSecurityContext getCxfClientSecurityContext(final KeyStore keystore, final char[] keyPassword, final KeyStore truststore, final Collection protocols, final Collection ciphers, final boolean enableHostnameVerification) throws GeneralSecurityException { final KeyManager[] keyManagers = getKeyManagers(keystore, keyPassword); final TrustManager[] trustManagers = getTrustManagers(truststore); return new CxfClientSecurityContext(keyManagers, trustManagers, protocols, ciphers, enableHostnameVerification); } /** * Factory method to get a context that will configure {@link AbstractCxfRestClient}'s SSL * Handshake context and hostname verifier as specified by the input provided * * @param sslSocketFactory * {@link SSLSocketFactory} to use when connecting to vCD/vCTA * @param enableHostnameVerification * whether hostname verification should be enabled or disabled * @return {@link CxfClientSecurityContext} that will help {@link AbstractCxfRestClient} to pick * up the {@link SSLSocketFactory} provided and configure hostname verification as * specified. */ public static CxfClientSecurityContext getCxfClientSecurityContext( final SSLSocketFactory sslSocketFactory, final boolean enableHostnameVerification) { return new CxfClientSecurityContext(sslSocketFactory, enableHostnameVerification); } /** * If a truststore is provided, initializes a new {@link TrustManager} that will be configured * to trust certificates and CA's present in the truststore * * @param truststore * {@link KeyStore} containing trust material; null chooses default * Trust Manager * @return a newly created {@link TrustManager} or null if default trust manager * must be used. * @throws KeyStoreException * If {@code truststore} contents cannot be accessed for some reason. */ private static TrustManager[] getTrustManagers(KeyStore truststore) throws KeyStoreException { if (truststore == null) { return DEFAULT_TRUST_MANAGER; } final TrustManagerFactory trustManagerFactory; try { trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); } catch (NoSuchAlgorithmException nsae) { throw new AssertionError("Required Trust Manager algorithm PKIX unavailable on this system", nsae); } trustManagerFactory.init(truststore); return trustManagerFactory.getTrustManagers(); } /** * If a keystore is provided, initializes a new {@link KeyManager} that will be configured with * key and certificates present in the keystore * * @param keystore * {@link KeyStore} containing key material; null chooses default Key * Manager * @param keyPassword * password to decode key material. Ignored if {@code keystore} is null * @return a newly created {@link KeyManager} or null if default key manager must * be used. * @throws GeneralSecurityException * If keystore contents cannot be accessed for some reason. */ private static KeyManager[] getKeyManagers(final KeyStore keystore, final char[] keyPassword) throws GeneralSecurityException { if (keystore == null) { return DEFAULT_KEY_MANAGER; } final KeyManagerFactory keyManagerFactory; try { keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); } catch (NoSuchAlgorithmException nsae) { throw new AssertionError("Required Key Manager algorithm SunX509 unavailable on this system", nsae); } keyManagerFactory.init(keystore, keyPassword); return keyManagerFactory.getKeyManagers(); } final boolean isHostnameVerificationEnabled() { return enableHostnameVerification; } public final SSLSocketFactory getSSLSocketFactory() { return sslSocketFactory; } private static String getTLSVersionForJava() { String javaVersionStr = System.getProperty("java.version"); final String[] versionParts = javaVersionStr.split("\\."); if (Integer.parseInt(versionParts[0]) == 1) { final int javaMajorVersion = Integer.parseInt(versionParts[1]); if (javaMajorVersion >= 8) { return "TLS"; } else if (javaMajorVersion == 7) { return "TLSv1.2"; } } throw new UnsupportedOperationException("Java version " + javaVersionStr + " is not supported." + " Java 1.7 or later required"); } private SSLSocketFactory createRestrictedSocketFactory(final KeyManager[] keyManagers, final TrustManager[] trustManagers, final Collection protocols, final Collection ciphers) throws GeneralSecurityException { final SSLContext restrictedSSLContext; restrictedSSLContext = SSLContext.getInstance(getTLSVersionForJava()); restrictedSSLContext.init(keyManagers, trustManagers, new SecureRandom()); return new RestrictedSSLSocketFactory(restrictedSSLContext.getSocketFactory(), protocols, ciphers); } @Override public int hashCode() { return Objects.hash(enableHostnameVerification, sslSocketFactory); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof CxfClientSecurityContext)) { return false; } CxfClientSecurityContext other = (CxfClientSecurityContext) obj; return Objects.equals(enableHostnameVerification, other.enableHostnameVerification) && Objects.equals(sslSocketFactory, other.sslSocketFactory); } @Override public String toString() { return String .format("[CxfClientSecurityContext] " + "SSLSocketFactory = %s,\n" + "Hostname verification is %s.", sslSocketFactory, enableHostnameVerification ? "enabled" : "disabled"); } /** * Wraps a {@link SSLSocketFactory} to ensure newly created sockets enforce preferred ciphers * and protocol use only. * * (This class is mostly similar to one in common-crypto, but due to project isolation, is * repeated here) */ private static final class RestrictedSSLSocketFactory extends SSLSocketFactory { private final SSLSocketFactory sslSocketFactory; private final Set protocols; private final String[] ciphers; public RestrictedSSLSocketFactory(final SSLSocketFactory sslSocketFactory, final Collection protocols, final Collection ciphers) { this.sslSocketFactory = sslSocketFactory; this.protocols = (protocols == null) ? null : new HashSet<>(protocols); this.ciphers = intersectionOf(ciphers, sslSocketFactory.getSupportedCipherSuites()); } @Override public String[] getDefaultCipherSuites() { return ciphers; } @Override public String[] getSupportedCipherSuites() { return ciphers; } @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { return restrict((SSLSocket) sslSocketFactory.createSocket(s, host, port, autoClose)); } @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException { return restrict((SSLSocket) sslSocketFactory.createSocket(host, port)); } @Override public Socket createSocket(InetAddress host, int port) throws IOException { return restrict((SSLSocket) sslSocketFactory.createSocket(host, port)); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { return restrict((SSLSocket) sslSocketFactory.createSocket(host, port, localHost, localPort)); } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { return restrict((SSLSocket) sslSocketFactory.createSocket(address, port, localAddress, localPort)); } private SSLSocket restrict(final SSLSocket socket) { socket.setEnabledProtocols(intersectionOf(protocols, socket.getEnabledProtocols())); socket.setEnabledCipherSuites(ciphers); return socket; } private static String[] intersectionOf(final Collection configured, final String[] supported) { if (configured == null) { return supported; } final Set resultSet = new HashSet<>(Arrays.asList(supported)); resultSet.retainAll(configured); return resultSet.toArray(new String[resultSet.size()]); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy