/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.TrustManagerImpl; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.util.Arrays; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; /** * The instances of this class encapsulate all the info * about enabled cipher suites and protocols, * as well as the information about client/server mode of * ssl socket, whether it require/want client authentication or not, * and controls whether new SSL sessions may be established by this * socket or not. */ public class SSLParametersImpl implements Cloneable { // default source of authentication keys private static volatile X509KeyManager defaultKeyManager; // default source of authentication trust decisions private static volatile X509TrustManager defaultTrustManager; // default source of random numbers private static volatile SecureRandom defaultSecureRandom; // default SSL parameters private static volatile SSLParametersImpl defaultParameters; // client session context contains the set of reusable // client-side SSL sessions private final ClientSessionContext clientSessionContext; // server session context contains the set of reusable // server-side SSL sessions private final ServerSessionContext serverSessionContext; // source of authentication keys private X509KeyManager keyManager; // source of authentication trust decisions private X509TrustManager trustManager; // source of random numbers private SecureRandom secureRandom; // cipher suites available for SSL connection private CipherSuite[] enabledCipherSuites; // string representations of available cipher suites private String[] enabledCipherSuiteNames = null; // protocols available for SSL connection private String[] enabledProtocols = ProtocolVersion.supportedProtocols; // if the peer with this parameters tuned to work in client mode private boolean client_mode = true; // if the peer with this parameters tuned to require client authentication private boolean need_client_auth = false; // if the peer with this parameters tuned to request client authentication private boolean want_client_auth = false; // if the peer with this parameters allowed to cteate new SSL session private boolean enable_session_creation = true; protected CipherSuite[] getEnabledCipherSuitesMember() { if (enabledCipherSuites == null) { this.enabledCipherSuites = CipherSuite.DEFAULT_CIPHER_SUITES; } return enabledCipherSuites; } /** * Initializes the parameters. Naturally this constructor is used * in SSLContextImpl.engineInit method which directly passes its * parameters. In other words this constructor holds all * the functionality provided by SSLContext.init method. * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[], * SecureRandom)} for more information */ protected SSLParametersImpl(KeyManager[] kms, TrustManager[] tms, SecureRandom sr, ClientSessionContext clientSessionContext, ServerSessionContext serverSessionContext) throws KeyManagementException { this.serverSessionContext = serverSessionContext; this.clientSessionContext = clientSessionContext; // It's not described by the spec of SSLContext what should happen // if the arrays of length 0 are specified. This implementation // behave as for null arrays (i.e. use installed security providers) // initialize keyManager if ((kms == null) || (kms.length == 0)) { keyManager = getDefaultKeyManager(); } else { keyManager = findX509KeyManager(kms); } // initialize trustManager if ((tms == null) || (tms.length == 0)) { trustManager = getDefaultTrustManager(); } else { trustManager = findX509TrustManager(tms); } // initialize secure random // BEGIN android-removed // if (sr == null) { // if (defaultSecureRandom == null) { // defaultSecureRandom = new SecureRandom(); // } // secureRandom = defaultSecureRandom; // } else { // secureRandom = sr; // } // END android-removed // BEGIN android-added // We simply use the SecureRandom passed in by the caller. If it's // null, we don't replace it by a new instance. The native code below // then directly accesses /dev/urandom. Not the most elegant solution, // but faster than going through the SecureRandom object. secureRandom = sr; // END android-added } protected static SSLParametersImpl getDefault() throws KeyManagementException { SSLParametersImpl result = defaultParameters; if (result == null) { // single-check idiom defaultParameters = result = new SSLParametersImpl(null, null, null, new ClientSessionContext(), new ServerSessionContext()); } return (SSLParametersImpl) result.clone(); } /** * @return server session context */ protected ServerSessionContext getServerSessionContext() { return serverSessionContext; } /** * @return client session context */ protected ClientSessionContext getClientSessionContext() { return clientSessionContext; } /** * @return key manager */ protected X509KeyManager getKeyManager() { return keyManager; } /** * @return trust manager */ protected X509TrustManager getTrustManager() { return trustManager; } /** * @return secure random */ protected SecureRandom getSecureRandom() { if (secureRandom != null) { return secureRandom; } SecureRandom result = defaultSecureRandom; if (result == null) { // single-check idiom defaultSecureRandom = result = new SecureRandom(); } secureRandom = result; return secureRandom; } /** * @return the secure random member reference, even it is null */ protected SecureRandom getSecureRandomMember() { return secureRandom; } /** * @return the names of enabled cipher suites */ protected String[] getEnabledCipherSuites() { if (enabledCipherSuiteNames == null) { CipherSuite[] enabledCipherSuites = getEnabledCipherSuitesMember(); enabledCipherSuiteNames = new String[enabledCipherSuites.length]; for (int i = 0; i< enabledCipherSuites.length; i++) { enabledCipherSuiteNames[i] = enabledCipherSuites[i].getName(); } } return enabledCipherSuiteNames.clone(); } /** * Sets the set of available cipher suites for use in SSL connection. * @param suites: String[] * @return */ protected void setEnabledCipherSuites(String[] suites) { if (suites == null) { throw new IllegalArgumentException("suites == null"); } CipherSuite[] cipherSuites = new CipherSuite[suites.length]; for (int i=0; i<suites.length; i++) { String suite = suites[i]; if (suite == null) { throw new IllegalArgumentException("suites[" + i + "] == null"); } cipherSuites[i] = CipherSuite.getByName(suite); if (cipherSuites[i] == null || !cipherSuites[i].supported) { throw new IllegalArgumentException(suite + " is not supported."); } } enabledCipherSuites = cipherSuites; enabledCipherSuiteNames = suites; } /** * @return the set of enabled protocols */ protected String[] getEnabledProtocols() { return enabledProtocols.clone(); } /** * Sets the set of available protocols for use in SSL connection. * @param protocols String[] */ protected void setEnabledProtocols(String[] protocols) { if (protocols == null) { throw new IllegalArgumentException("protocols == null"); } for (int i=0; i<protocols.length; i++) { String protocol = protocols[i]; if (protocol == null) { throw new IllegalArgumentException("protocols[" + i + "] == null"); } if (!ProtocolVersion.isSupported(protocol)) { throw new IllegalArgumentException("Protocol " + protocol + " is not supported."); } } enabledProtocols = protocols; } /** * Tunes the peer holding this parameters to work in client mode. * @param mode if the peer is configured to work in client mode */ protected void setUseClientMode(boolean mode) { client_mode = mode; } /** * Returns the value indicating if the parameters configured to work * in client mode. */ protected boolean getUseClientMode() { return client_mode; } /** * Tunes the peer holding this parameters to require client authentication */ protected void setNeedClientAuth(boolean need) { need_client_auth = need; // reset the want_client_auth setting want_client_auth = false; } /** * Returns the value indicating if the peer with this parameters tuned * to require client authentication */ protected boolean getNeedClientAuth() { return need_client_auth; } /** * Tunes the peer holding this parameters to request client authentication */ protected void setWantClientAuth(boolean want) { want_client_auth = want; // reset the need_client_auth setting need_client_auth = false; } /** * Returns the value indicating if the peer with this parameters * tuned to request client authentication * @return */ protected boolean getWantClientAuth() { return want_client_auth; } /** * Allows/disallows the peer holding this parameters to * create new SSL session */ protected void setEnableSessionCreation(boolean flag) { enable_session_creation = flag; } /** * Returns the value indicating if the peer with this parameters * allowed to cteate new SSL session */ protected boolean getEnableSessionCreation() { return enable_session_creation; } /** * Returns the clone of this object. * @return the clone. */ @Override protected Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } private static X509KeyManager getDefaultKeyManager() throws KeyManagementException { X509KeyManager result = defaultKeyManager; if (result == null) { // single-check idiom defaultKeyManager = result = createDefaultKeyManager(); } return result; } private static X509KeyManager createDefaultKeyManager() throws KeyManagementException { try { String algorithm = KeyManagerFactory.getDefaultAlgorithm(); KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); kmf.init(null, null); KeyManager[] kms = kmf.getKeyManagers(); return findX509KeyManager(kms); } catch (NoSuchAlgorithmException e) { throw new KeyManagementException(e); } catch (KeyStoreException e) { throw new KeyManagementException(e); } catch (UnrecoverableKeyException e) { throw new KeyManagementException(e); } } private static X509KeyManager findX509KeyManager(KeyManager[] kms) throws KeyManagementException { for (KeyManager km : kms) { if (km instanceof X509KeyManager) { return (X509KeyManager)km; } } throw new KeyManagementException("Failed to find an X509KeyManager in " + Arrays.toString(kms)); } /** * Gets the default trust manager. * * TODO: Move this to a published API under dalvik.system. */ public static X509TrustManager getDefaultTrustManager() throws KeyManagementException { X509TrustManager result = defaultTrustManager; if (result == null) { // single-check idiom defaultTrustManager = result = createDefaultTrustManager(); } return result; } private static X509TrustManager createDefaultTrustManager() throws KeyManagementException { try { String algorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); tmf.init((KeyStore) null); TrustManager[] tms = tmf.getTrustManagers(); X509TrustManager trustManager = findX509TrustManager(tms); return trustManager; } catch (NoSuchAlgorithmException e) { throw new KeyManagementException(e); } catch (KeyStoreException e) { throw new KeyManagementException(e); } } private static X509TrustManager findX509TrustManager(TrustManager[] tms) throws KeyManagementException { for (TrustManager tm : tms) { if (tm instanceof X509TrustManager) { return (X509TrustManager)tm; } } throw new KeyManagementException("Failed to find an X509TrustManager in " + Arrays.toString(tms)); } }