blob: e7b0a035474ed31cdfc7b361d82509fdfc9efa90 [file] [log] [blame]
//========================================================================
//Copyright (c) Webtide LLC
//------------------------------------------------------------------------
//All rights reserved. This program and the accompanying materials
//are made available under the terms of the Eclipse Public License v1.0
//and Apache License v2.0 which accompanies this distribution.
//
//The Eclipse Public License is available at
//http://www.eclipse.org/legal/epl-v10.html
//
//The Apache License v2.0 is available at
//http://www.apache.org/licenses/LICENSE-2.0.txt
//
//You may elect to redistribute this code under either of these licenses.
//========================================================================
package org.eclipse.jetty.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CRL;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import org.eclipse.jetty.exssl.AliasedX509ExtendedKeyManager;
import org.eclipse.jetty.http.security.Password;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.security.CertificateValidator;
public class SslContextFactory
{
public static final String DEFAULT_KEYMANAGERFACTORY_ALGORITHM =
(Security.getProperty("ssl.KeyManagerFactory.algorithm") == null ?
"SunX509" : Security.getProperty("ssl.KeyManagerFactory.algorithm"));
public static final String DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM =
(Security.getProperty("ssl.TrustManagerFactory.algorithm") == null ?
"SunX509" : Security.getProperty("ssl.TrustManagerFactory.algorithm"));
/** Default value for the keystore location path. */
public static final String DEFAULT_KEYSTORE_PATH =
System.getProperty("user.home") + File.separator + ".keystore";
/** String name of key password property. */
public static final String KEYPASSWORD_PROPERTY = "org.eclipse.jetty.ssl.keypassword";
/** String name of keystore password property. */
public static final String PASSWORD_PROPERTY = "org.eclipse.jetty.ssl.password";
/** Excluded cipher suites. */
private Set<String> _excludeCipherSuites = null;
/** Included cipher suites. */
private Set<String> _includeCipherSuites = null;
/** Keystore path. */
private String _keystorePath;
/** Keystore provider name */
private String _keystoreProvider;
/** Keystore type */
private String _keystoreType = "JKS";
/** Keystore input stream */
private InputStream _keystoreInputStream;
/** SSL certificate alias */
private String _certAlias;
/** Truststore path */
private String _truststorePath;
/** Truststore provider name */
private String _truststoreProvider;
/** Truststore type */
private String _truststoreType = "JKS";
/** Truststore input stream */
private InputStream _truststoreInputStream;
/** Set to true if client certificate authentication is required */
private boolean _needClientAuth = false;
/** Set to true if client certificate authentication is desired */
private boolean _wantClientAuth = false;
/** Set to true if SSL certificate validation is required */
private boolean _validateCerts;
/** Set to true if renegotiation is allowed */
private boolean _allowRenegotiate = false;
/** KeyStore password */
private transient Password _keystorePassword;
/** Key password */
private transient Password _keymanagerPassword;
/** TrustStore password */
private transient Password _truststorePassword;
/** SSL Provider name */
private String _sslProvider;
/** SSL Protocol name */
private String _sslProtocol = "TLS";
/** SecureRandom algorithm */
private String _secureRandomAlgorithm;
/** KeyManager factory algorithm */
private String _keyManagerFactoryAlgorithm = DEFAULT_KEYMANAGERFACTORY_ALGORITHM;
/** TrustManager factory algorithm */
private String _trustManagerFactoryAlgorithm = DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM;
/** Path to file that contains Certificate Revocation List */
private String _crlPath;
/** Maximum certification path length (n - number of intermediate certs, -1 for unlimited) */
private int _maxCertPathLength = -1;
/** SSL context */
private SSLContext _context;
public SslContextFactory() {}
public SslContextFactory(String keystorePath)
{
_keystorePath = keystorePath;
}
/* ------------------------------------------------------------ */
/**
* @return The array of cipher suite names to exclude from
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public String[] getExcludeCipherSuites()
{
return _excludeCipherSuites.toArray(new String[_excludeCipherSuites.size()]);
}
/* ------------------------------------------------------------ */
/**
* @param cipherSuites
* The array of cipher suite names to exclude from
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public void setExcludeCipherSuites(String[] cipherSuites)
{
_excludeCipherSuites = new HashSet<String>(Arrays.asList(cipherSuites));
}
/* ------------------------------------------------------------ */
/**
* @return The array of cipher suite names to include in
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public String[] getIncludeCipherSuites()
{
return _includeCipherSuites.toArray(new String[_includeCipherSuites.size()]);
}
/* ------------------------------------------------------------ */
/**
* @param cipherSuites
* The array of cipher suite names to include in
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public void setIncludeCipherSuites(String[] cipherSuites)
{
_includeCipherSuites = new HashSet<String>(Arrays.asList(cipherSuites));
}
/* ------------------------------------------------------------ */
/**
* @return The file or URL of the SSL Key store.
*/
public String getKeystore()
{
return _keystorePath;
}
/* ------------------------------------------------------------ */
/**
* @param keystore
* The file or URL of the SSL Key store.
*/
public void setKeystore(String keystore)
{
_keystorePath = keystore;
}
/* ------------------------------------------------------------ */
/**
* @return The provider of the key store
*/
public String getKeystoreProvider()
{
return _keystoreProvider;
}
/* ------------------------------------------------------------ */
/**
* @param keystoreProvider
* The provider of the key store
*/
public void setKeystoreProvider(String keystoreProvider)
{
_keystoreProvider = keystoreProvider;
}
/* ------------------------------------------------------------ */
/**
* @return The type of the key store (default "JKS")
*/
public String getKeystoreType()
{
return (_keystoreType);
}
/* ------------------------------------------------------------ */
/**
* @param keystoreType
* The type of the key store (default "JKS")
*/
public void setKeystoreType(String keystoreType)
{
_keystoreType = keystoreType;
}
/* ------------------------------------------------------------ */
/** Get the _keystoreInputStream.
* @return the _keystoreInputStream
*/
public InputStream getKeystoreInputStream()
{
checkConfig();
return _keystoreInputStream;
}
/* ------------------------------------------------------------ */
/** Set the _keystoreInputStream.
* @param _keystoreInputStream the _keystoreInputStream to set
*/
public void setKeystoreInputStream(InputStream keystoreInputStream)
{
_keystoreInputStream = keystoreInputStream;
}
/* ------------------------------------------------------------ */
/**
* @return Alias of SSL certificate for the connector
*/
public String getCertAlias()
{
return _certAlias;
}
/* ------------------------------------------------------------ */
/**
* @param certAlias
* Alias of SSL certificate for the connector
*/
public void setCertAlias(String certAlias)
{
_certAlias = certAlias;
}
/* ------------------------------------------------------------ */
/**
* @return The file name or URL of the trust store location
*/
public String getTruststore()
{
return _truststorePath;
}
/* ------------------------------------------------------------ */
/**
* @param truststore
* The file name or URL of the trust store location
*/
public void setTruststore(String truststore)
{
_truststorePath = truststore;
}
/* ------------------------------------------------------------ */
/**
* @return The provider of the trust store
*/
public String getTruststoreProvider()
{
return _truststoreProvider;
}
/* ------------------------------------------------------------ */
/**
* @param truststoreProvider
* The provider of the trust store
*/
public void setTruststoreProvider(String truststoreProvider)
{
_truststoreProvider = truststoreProvider;
}
/* ------------------------------------------------------------ */
/**
* @return The type of the trust store (default "JKS")
*/
public String getTruststoreType()
{
return _truststoreType;
}
/* ------------------------------------------------------------ */
/**
* @param truststoreType
* The type of the trust store (default "JKS")
*/
public void setTruststoreType(String truststoreType)
{
_truststoreType = truststoreType;
}
/* ------------------------------------------------------------ */
/** Get the _truststoreInputStream.
* @return the _truststoreInputStream
*/
public InputStream getTruststoreInputStream()
{
checkConfig();
return _truststoreInputStream;
}
/* ------------------------------------------------------------ */
/** Set the _truststoreInputStream.
* @param _truststoreInputStream the _truststoreInputStream to set
*/
public void setTruststoreInputStream(InputStream truststoreInputStream)
{
_truststoreInputStream = truststoreInputStream;
}
/* ------------------------------------------------------------ */
/**
* @return True if SSL needs client authentication.
* @see SSLEngine#getNeedClientAuth()
*/
public boolean getNeedClientAuth()
{
return _needClientAuth;
}
/* ------------------------------------------------------------ */
/**
* @param needClientAuth
* True if SSL needs client authentication.
* @see SSLEngine#getNeedClientAuth()
*/
public void setNeedClientAuth(boolean needClientAuth)
{
_needClientAuth = needClientAuth;
}
/* ------------------------------------------------------------ */
/**
* @return True if SSL wants client authentication.
* @see SSLEngine#getWantClientAuth()
*/
public boolean getWantClientAuth()
{
return _wantClientAuth;
}
/* ------------------------------------------------------------ */
/**
* @param wantClientAuth
* True if SSL wants client authentication.
* @see SSLEngine#getWantClientAuth()
*/
public void setWantClientAuth(boolean wantClientAuth)
{
_wantClientAuth = wantClientAuth;
}
/* ------------------------------------------------------------ */
/**
* @return true if SSL certificate has to be validated
*/
public boolean getValidateCerts()
{
return _validateCerts;
}
/* ------------------------------------------------------------ */
/**
* @param validateServerCert
* true if SSL certificate has to be validated
*/
public void setValidateCerts(boolean validateCerts)
{
_validateCerts = validateCerts;
}
/* ------------------------------------------------------------ */
/**
* @return True if SSL re-negotiation is allowed (default false)
*/
public boolean isAllowRenegotiate()
{
return _allowRenegotiate;
}
/* ------------------------------------------------------------ */
/**
* Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered a vulnerability
* in SSL/TLS with re-negotiation. If your JVM does not have CVE-2009-3555 fixed,
* then re-negotiation should not be allowed.
*
* @param allowRenegotiate
* true if re-negotiation is allowed (default false)
*/
public void setAllowRenegotiate(boolean allowRenegotiate)
{
_allowRenegotiate = allowRenegotiate;
}
/* ------------------------------------------------------------ */
/**
* @param password
* The password for the key store
*/
public void setKeystorePassword(String password)
{
_keystorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
}
/* ------------------------------------------------------------ */
/**
* @param password
* The password (if any) for the specific key within the key store
*/
public void setKeyManagerPassword(String password)
{
_keymanagerPassword = Password.getPassword(KEYPASSWORD_PROPERTY,password,null);
}
/* ------------------------------------------------------------ */
/**
* @param password
* The password for the trust store
*/
public void setTruststorePassword(String password)
{
_truststorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
}
/* ------------------------------------------------------------ */
/**
* @return The SSL provider name, which if set is passed to
* {@link SSLContext#getInstance(String, String)}
*/
public String getProvider()
{
return _sslProvider;
}
/* ------------------------------------------------------------ */
/**
* @param provider
* The SSL provider name, which if set is passed to
* {@link SSLContext#getInstance(String, String)}
*/
public void setProvider(String provider)
{
_sslProvider = provider;
}
/* ------------------------------------------------------------ */
/**
* @return The SSL protocol (default "TLS") passed to
* {@link SSLContext#getInstance(String, String)}
*/
public String getProtocol()
{
return _sslProtocol;
}
/* ------------------------------------------------------------ */
/**
* @param protocol
* The SSL protocol (default "TLS") passed to
* {@link SSLContext#getInstance(String, String)}
*/
public void setProtocol(String protocol)
{
_sslProtocol = protocol;
}
/* ------------------------------------------------------------ */
/**
* @return The algorithm name, which if set is passed to
* {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to
* {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)}
*/
public String getSecureRandomAlgorithm()
{
return _secureRandomAlgorithm;
}
/* ------------------------------------------------------------ */
/**
* @param algorithm
* The algorithm name, which if set is passed to
* {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to
* {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)}
*/
public void setSecureRandomAlgorithm(String algorithm)
{
_secureRandomAlgorithm = algorithm;
}
/* ------------------------------------------------------------ */
/**
* @return The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
*/
public String getSslKeyManagerFactoryAlgorithm()
{
return (_keyManagerFactoryAlgorithm);
}
/* ------------------------------------------------------------ */
/**
* @param algorithm
* The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
*/
public void setSslKeyManagerFactoryAlgorithm(String algorithm)
{
_keyManagerFactoryAlgorithm = algorithm;
}
/* ------------------------------------------------------------ */
/**
* @return The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
*/
public String getTrustManagerFactoryAlgorithm()
{
return (_trustManagerFactoryAlgorithm);
}
/* ------------------------------------------------------------ */
/**
* @param algorithm
* The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
*/
public void setTrustManagerFactoryAlgorithm(String algorithm)
{
_trustManagerFactoryAlgorithm = algorithm;
}
/* ------------------------------------------------------------ */
/**
* @return Path to file that contains Certificate Revocation List
*/
public String getCrlPath()
{
return _crlPath;
}
/* ------------------------------------------------------------ */
/**
* @param crlPath
* Path to file that contains Certificate Revocation List
*/
public void setCrlPath(String crlPath)
{
_crlPath = crlPath;
}
/* ------------------------------------------------------------ */
/**
* @return Maximum number of intermediate certificates in
* the certification path (-1 for unlimited)
*/
public int getMaxCertPathLength()
{
return _maxCertPathLength;
}
/* ------------------------------------------------------------ */
/**
* @param maxCertPathLength
* maximum number of intermediate certificates in
* the certification path (-1 for unlimited)
*/
public void setMaxCertPathLength(int maxCertPathLength)
{
_maxCertPathLength = maxCertPathLength;
}
/* ------------------------------------------------------------ */
/**
* @return The SSLContext
*/
public SSLContext getSslContext()
{
try
{
if (_context == null)
_context = createSSLContext();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return _context;
}
/* ------------------------------------------------------------ */
/**
* @param sslContext
* Set a preconfigured SSLContext
*/
public void setSslContext(SSLContext sslContext)
{
_context = sslContext;
}
/* ------------------------------------------------------------ */
/**
* @return The SSLContext
*/
public SSLContext getClientSslContext()
{
try
{
if (_context == null)
_context = createClientSSLContext();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return _context;
}
/* ------------------------------------------------------------ */
/**
* @return SSL context for the client code
*/
public SSLContext createClientSSLContext() throws Exception
{
SSLContext sslContext = null;
if (_keystoreInputStream == null && _keystorePath == null &&
_truststoreInputStream == null && _truststorePath == null )
{
// Create a trust manager that does not validate certificate chains
TrustManager trustAllCerts = new X509TrustManager()
{
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
};
sslContext = SSLContext.getInstance(_sslProtocol);
sslContext.init(null, new TrustManager[]{trustAllCerts}, null);
}
else
{
sslContext = createSSLContext();
}
return sslContext;
}
/* ------------------------------------------------------------ */
public SSLContext createSSLContext() throws Exception
{
// verify that keystore and truststore
// parameters are set up correctly
checkConfig();
KeyStore keyStore = getKeyStore(_keystoreInputStream, _keystorePath, _keystoreType,
_keystoreProvider, _keystorePassword==null? null: _keystorePassword.toString());
KeyStore trustStore = getKeyStore(_truststoreInputStream, _truststorePath, _truststoreType,
_truststoreProvider, _truststorePassword==null? null: _truststorePassword.toString());
Collection<? extends CRL> crls = loadCRL(_crlPath);
if (_validateCerts && keyStore != null)
{
if (_certAlias == null)
{
List<String> aliases = Collections.list(keyStore.aliases());
_certAlias = aliases.size() == 1 ? aliases.get(0) : null;
}
Certificate cert = _certAlias == null?null:keyStore.getCertificate(_certAlias);
if (cert == null)
{
throw new Exception("No certificate found in the keystore" + (_certAlias==null ? "":" for alias " + _certAlias));
}
CertificateValidator validator = new CertificateValidator(trustStore,crls);
validator.validate(keyStore, cert);
}
KeyManager[] keyManagers = getKeyManagers(keyStore);
TrustManager[] trustManagers = getTrustManagers(trustStore,crls);
SecureRandom secureRandom = _secureRandomAlgorithm == null?null:SecureRandom.getInstance(_secureRandomAlgorithm);
SSLContext context = _sslProvider == null?SSLContext.getInstance(_sslProtocol):SSLContext.getInstance(_sslProtocol,_sslProvider);
context.init(keyManagers,trustManagers,secureRandom);
return context;
}
/* ------------------------------------------------------------ */
protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception
{
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_keyManagerFactoryAlgorithm);
keyManagerFactory.init(keyStore,_keymanagerPassword == null?(_keystorePassword == null?null:_keystorePassword.toString().toCharArray()):_keymanagerPassword.toString().toCharArray());
KeyManager[] managers = keyManagerFactory.getKeyManagers();
if (_certAlias != null)
{
for (int idx = 0; idx < managers.length; idx++)
{
if (managers[idx] instanceof X509KeyManager)
{
managers[idx] = new AliasedX509ExtendedKeyManager(_certAlias,(X509KeyManager)managers[idx]);
}
}
}
return managers;
}
/* ------------------------------------------------------------ */
protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection<? extends CRL> crls) throws Exception
{
TrustManager[] managers = null;
if (trustStore != null && _validateCerts)
{
// Revocation checking is only supported for PKIX algorithm
if (_trustManagerFactoryAlgorithm.equalsIgnoreCase("PKIX"))
{
PKIXBuilderParameters pbParams = new PKIXBuilderParameters(trustStore,new X509CertSelector());
// Enable revocation checking
pbParams.setRevocationEnabled(true);
// Set maximum certification path length
pbParams.setMaxPathLength(_maxCertPathLength);
if (crls != null && !crls.isEmpty())
{
pbParams.addCertStore(CertStore.getInstance("Collection",new CollectionCertStoreParameters(crls)));
}
// Enable On-Line Certificate Status Protocol (OCSP) support
Security.setProperty("ocsp.enable","true");
// Enable Certificate Revocation List Distribution Points (CRLDP) support
System.setProperty("com.sun.security.enableCRLDP","true");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm);
trustManagerFactory.init(new CertPathTrustManagerParameters(pbParams));
managers = trustManagerFactory.getTrustManagers();
}
else
{
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm);
trustManagerFactory.init(trustStore);
managers = trustManagerFactory.getTrustManagers();
}
}
return managers;
}
/* ------------------------------------------------------------ */
protected KeyStore getKeyStore(InputStream storeStream, String storePath, String storeType, String storeProvider, String storePassword) throws Exception
{
KeyStore keystore = null;
if (storeStream != null || storePath != null)
{
InputStream inStream = storeStream;
try
{
if (inStream == null)
{
inStream = Resource.newResource(storePath).getInputStream();
}
if (storeProvider != null)
{
keystore = KeyStore.getInstance(storeType, storeProvider);
}
else
{
keystore = KeyStore.getInstance(storeType);
}
keystore.load(inStream, storePassword == null ? null : storePassword.toCharArray());
}
finally
{
if (inStream != null)
{
inStream.close();
}
}
}
return keystore;
}
/* ------------------------------------------------------------ */
protected Collection<? extends CRL> loadCRL(String crlPath) throws Exception
{
Collection<? extends CRL> crlList = null;
if (crlPath != null)
{
InputStream in = null;
try
{
in = Resource.newResource(crlPath).getInputStream();
crlList = CertificateFactory.getInstance("X.509").generateCRLs(in);
}
finally
{
if (in != null)
{
in.close();
}
}
}
return crlList;
}
protected void checkConfig()
{
/*
* if the keystore exists but the trust store
* does not, use the keystore as the trust store
*/
if (_keystoreInputStream != null || _keystorePath != null)
{
if (_truststoreInputStream == null && _truststorePath == null)
{
_truststorePath = _keystorePath;
_truststoreInputStream = _keystoreInputStream;
_truststoreType = _keystoreType;
_truststoreProvider = _keystoreProvider;
_truststorePassword = _keystorePassword;
_trustManagerFactoryAlgorithm = _keyManagerFactoryAlgorithm;
}
}
// It's the same stream we cannot read it twice, so read it once in memory
if (_keystoreInputStream != null && _keystoreInputStream == _truststoreInputStream)
{
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IO.copy(_keystoreInputStream, baos);
_keystoreInputStream.close();
_keystoreInputStream = new ByteArrayInputStream(baos.toByteArray());
_truststoreInputStream = new ByteArrayInputStream(baos.toByteArray());
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
}
}
public String[] selectCipherSuites(String[] enabledCipherSuites, String[] supportedCipherSuites)
{
Set<String> selectedCipherSuites = null;
if (enabledCipherSuites != null)
{
selectedCipherSuites = new HashSet<String>(Arrays.asList(enabledCipherSuites));
}
else
{
selectedCipherSuites = new HashSet<String>();
}
if ((supportedCipherSuites != null && supportedCipherSuites.length > 0) && (_includeCipherSuites != null && _includeCipherSuites.size() > 0))
{
Set<String> supportedCSList = new HashSet<String>(Arrays.asList(supportedCipherSuites));
for (String cipherName : _includeCipherSuites)
{
if ((!selectedCipherSuites.contains(cipherName)) && supportedCSList.contains(cipherName))
{
selectedCipherSuites.add(cipherName);
}
}
}
if (_excludeCipherSuites != null && _excludeCipherSuites.size() > 0)
{
for (String cipherName : _excludeCipherSuites)
{
if (selectedCipherSuites.contains(cipherName))
{
selectedCipherSuites.remove(cipherName);
}
}
}
return selectedCipherSuites.toArray(new String[selectedCipherSuites.size()]);
}
}