blob: f6db4b2adab2f686da025044c068b4a8d9da67d6 [file] [log] [blame]
/*
* 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 java.security.InvalidAlgorithmParameterException;
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.security.cert.CertificateEncodingException;
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 incapsulate 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
// BEGIN android-changed
private final ClientSessionContext clientSessionContext;
// server session context contains the set of reusable
// server-side SSL sessions
private final ServerSessionContext serverSessionContext;
// END android-changed
// 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
// BEGIN android-changed
private CipherSuite[] enabledCipherSuites;
// END android-changed
// 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;
// BEGIN android-changed
protected CipherSuite[] getEnabledCipherSuitesMember() {
if (enabledCipherSuites == null) {
this.enabledCipherSuites = CipherSuite.DEFAULT_CIPHER_SUITES;
}
return enabledCipherSuites;
}
// END android-changed
/**
* 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,
// BEGIN android-changed
SecureRandom sr, ClientSessionContext clientSessionContext,
ServerSessionContext serverSessionContext)
throws KeyManagementException {
this.serverSessionContext = serverSessionContext;
this.clientSessionContext = clientSessionContext;
// END android-changed
// 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);
}
if (keyManager == null) {
throw new KeyManagementException("No X509KeyManager found");
}
// initialize trustManager
if ((tms == null) || (tms.length == 0)) {
trustManager = getDefaultTrustManager();
} else {
trustManager = findX509TrustManager(tms);
}
if (trustManager == null) {
throw new KeyManagementException("No X509TrustManager found");
}
// 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
*/
// BEGIN android-changed
protected ServerSessionContext getServerSessionContext() {
// END android-changed
return serverSessionContext;
}
/**
* @return client session context
*/
// BEGIN android-changed
protected ClientSessionContext getClientSessionContext() {
// END android-changed
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;
}
// BEGIN android-added
/**
* @return the secure random member reference, even it is null
*/
protected SecureRandom getSecureRandomMember() {
return secureRandom;
}
// END android-added
/**
* @return the names of enabled cipher suites
*/
protected String[] getEnabledCipherSuites() {
if (enabledCipherSuiteNames == null) {
// BEGIN android-added
CipherSuite[] enabledCipherSuites = getEnabledCipherSuitesMember();
// END android-added
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() {
// BEGIN android-changed
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
// END android-changed
}
private static X509KeyManager getDefaultKeyManager() {
X509KeyManager result = defaultKeyManager;
if (result == null) {
// single-check idiom
defaultKeyManager = result = createDefaultKeyManager();
}
return result;
}
private static X509KeyManager createDefaultKeyManager() {
try {
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(null, null);
KeyManager[] kms = kmf.getKeyManagers();
return findX509KeyManager(kms);
} catch (NoSuchAlgorithmException e) {
return null;
} catch (KeyStoreException e) {
return null;
} catch (UnrecoverableKeyException e) {
return null;
}
}
private static X509KeyManager findX509KeyManager(KeyManager[] kms) {
for (KeyManager km : kms) {
if (km instanceof X509KeyManager) {
return (X509KeyManager)km;
}
}
return null;
}
/**
* Gets the default trust manager.
*
* TODO: Move this to a published API under dalvik.system.
*/
public static X509TrustManager getDefaultTrustManager() {
X509TrustManager result = defaultTrustManager;
if (result == null) {
// single-check idiom
defaultTrustManager = result = createDefaultTrustManager();
}
return result;
}
private static X509TrustManager createDefaultTrustManager() {
try {
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init((KeyStore) null);
TrustManager[] tms = tmf.getTrustManagers();
X509TrustManager trustManager = findX509TrustManager(tms);
// BEGIN android-added
if (trustManager instanceof TrustManagerImpl) {
((TrustManagerImpl) trustManager).indexTrustAnchors();
}
// END android-added
return trustManager;
} catch (NoSuchAlgorithmException e) {
return null;
} catch (KeyStoreException e) {
return null;
} catch (CertificateEncodingException e) {
return null;
} catch (InvalidAlgorithmParameterException e) {
return null;
}
}
private static X509TrustManager findX509TrustManager(TrustManager[] tms) {
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager)tm;
}
}
return null;
}
}