A continuación se muestra la clase de Java que utilizo para hacer cumplir las suites de cifrado y los protocolos. Antes de SSLSocketFactoryEx
, estaba modificando las propiedades en el SSLSocket
cuando tenía acceso a ellas. La gente de Java en Stack Overflow lo ayudó, así que es bueno poder publicarlo aquí.
SSLSocketFactoryEx
prefiere paquetes de cifrado más fuertes (como ECDHE
y DHE
), y se omite conjuntos de cifrado débiles y heridos (como RC4
y MD5
). Tiene que habilitar cuatro cifras de transporte de clave RSA para la interoperabilidad con Google y Microsoft cuando TLS 1.2 es no disponible. Son TLS_RSA_WITH_AES_256_CBC_SHA256
, TLS_RSA_WITH_AES_256_CBC_SHA
y dos amigos. Si es posible, debe eliminar los esquemas de transporte de claves TLS_RSA_*
.
Mantenga la lista de la suite de cifrado lo más pequeña posible. Si anuncia todos los cifrados disponibles (similar a la lista de Flaschen), entonces su lista será 80+. Eso ocupa 160 bytes en el ClientHello
, y puede hacer que algunos dispositivos fallen porque tienen un pequeño búfer de tamaño fijo para procesar el ClientHello
. Los dispositivos rotos incluyen F5 y Ironport.
En la práctica, la lista del código siguiente se vincula a 10 o 15 suites de cifrado una vez que la lista preferida se cruza con las suites de cifrado admitidas por Java. Por ejemplo, esta es la lista que recibo cuando me preparo para conectarme o microsoft.com o google.com con una política ilimitada JCE en su lugar:
- TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA25 6
- TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
- TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
- TLS_DHE_RSA_WITH_AES_128_CBC_SHA
- TLS_DHE_DSS_WITH_AES_128_CBC_SHA
- TLS_RSA_WITH_AES_256_CBC_SHA256
- TLS_RSA_WITH_AES_256_CBC_SHA
- TLS_RSA_WITH_AES_128_CBC_SHA256
- TLS_RSA_WITH_AES_128_CBC_SHA
La lista omite algoritmos débiles/heridos, como RC4 y MD5. Si están habilitados, es probable que obtenga un Obsolete cryptography warning from Browser en alguna ocasión.
La lista será más pequeña con la política de JCE predeterminada porque la política elimina AES-256 y algunos otros. Creo que se trata de 7 suites de cifrado con la política restringida.
La clase SSLSocketFactoryEx
también garantiza que se utilizan protocolos TLS 1.0 y superiores. Los clientes Java anteriores a Java 8 deshabilitan TLS 1.1 y 1.2. SSLContext.getInstance("TLS")
también se colarán en SSLv3
(incluso en Java 8), por lo que hay que tomar medidas para eliminarlo.
Finalmente, la clase a continuación es compatible con TLS 1.3, por lo que debería funcionar cuando el proveedor los ponga a disposición. Las suites de cifrado *_CHACHA20_POLY1305
son preferibles si están disponibles porque son mucho más rápidas que algunas de las suites actuales y tienen mejores propiedades de seguridad. Google ya lo implementó en sus servidores. No estoy seguro de cuándo Oracle los proporcionará. OpenSSL les proporcionará OpenSSL
1.0.2
1.1.0.
Se puede utilizar de este modo:
URL url = new URL("https://www.google.com:443");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
SSLSocketFactoryEx factory = new SSLSocketFactoryEx();
connection.setSSLSocketFactory(factory);
connection.setRequestProperty("charset", "utf-8");
InputStream input = connection.getInputStream();
InputStreamReader reader = new InputStreamReader(input, "utf-8");
BufferedReader buffer = new BufferedReader(reader);
...
class SSLSocketFactoryEx extends SSLSocketFactory
{
public SSLSocketFactoryEx() throws NoSuchAlgorithmException, KeyManagementException
{
initSSLSocketFactoryEx(null,null,null);
}
public SSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws NoSuchAlgorithmException, KeyManagementException
{
initSSLSocketFactoryEx(km, tm, random);
}
public SSLSocketFactoryEx(SSLContext ctx) throws NoSuchAlgorithmException, KeyManagementException
{
initSSLSocketFactoryEx(ctx);
}
public String[] getDefaultCipherSuites()
{
return m_ciphers;
}
public String[] getSupportedCipherSuites()
{
return m_ciphers;
}
public String[] getDefaultProtocols()
{
return m_protocols;
}
public String[] getSupportedProtocols()
{
return m_protocols;
}
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException
{
SSLSocketFactory factory = m_ctx.getSocketFactory();
SSLSocket ss = (SSLSocket)factory.createSocket(s, host, port, autoClose);
ss.setEnabledProtocols(m_protocols);
ss.setEnabledCipherSuites(m_ciphers);
return ss;
}
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException
{
SSLSocketFactory factory = m_ctx.getSocketFactory();
SSLSocket ss = (SSLSocket)factory.createSocket(address, port, localAddress, localPort);
ss.setEnabledProtocols(m_protocols);
ss.setEnabledCipherSuites(m_ciphers);
return ss;
}
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException
{
SSLSocketFactory factory = m_ctx.getSocketFactory();
SSLSocket ss = (SSLSocket)factory.createSocket(host, port, localHost, localPort);
ss.setEnabledProtocols(m_protocols);
ss.setEnabledCipherSuites(m_ciphers);
return ss;
}
public Socket createSocket(InetAddress host, int port) throws IOException
{
SSLSocketFactory factory = m_ctx.getSocketFactory();
SSLSocket ss = (SSLSocket)factory.createSocket(host, port);
ss.setEnabledProtocols(m_protocols);
ss.setEnabledCipherSuites(m_ciphers);
return ss;
}
public Socket createSocket(String host, int port) throws IOException
{
SSLSocketFactory factory = m_ctx.getSocketFactory();
SSLSocket ss = (SSLSocket)factory.createSocket(host, port);
ss.setEnabledProtocols(m_protocols);
ss.setEnabledCipherSuites(m_ciphers);
return ss;
}
private void initSSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random)
throws NoSuchAlgorithmException, KeyManagementException
{
m_ctx = SSLContext.getInstance("TLS");
m_ctx.init(km, tm, random);
m_protocols = GetProtocolList();
m_ciphers = GetCipherList();
}
private void initSSLSocketFactoryEx(SSLContext ctx)
throws NoSuchAlgorithmException, KeyManagementException
{
m_ctx = ctx;
m_protocols = GetProtocolList();
m_ciphers = GetCipherList();
}
protected String[] GetProtocolList()
{
String[] preferredProtocols = { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" };
String[] availableProtocols = null;
SSLSocket socket = null;
try
{
SSLSocketFactory factory = m_ctx.getSocketFactory();
socket = (SSLSocket)factory.createSocket();
availableProtocols = socket.getSupportedProtocols();
Arrays.sort(availableProtocols);
}
catch(Exception e)
{
return new String[]{ "TLSv1" };
}
finally
{
if(socket != null)
socket.close();
}
List<String> aa = new ArrayList<String>();
for(int i = 0; i < preferredProtocols.length; i++)
{
int idx = Arrays.binarySearch(availableProtocols, preferredProtocols[i]);
if(idx >= 0)
aa.add(preferredProtocols[i]);
}
return aa.toArray(new String[0]);
}
protected String[] GetCipherList()
{
String[] preferredCiphers = {
// *_CHACHA20_POLY1305 are 3x to 4x faster than existing cipher suites.
// http://googleonlinesecurity.blogspot.com/2014/04/speeding-up-and-strengthening-https.html
// Use them if available. Normative names can be found at (TLS spec depends on IPSec spec):
// http://tools.ietf.org/html/draft-nir-ipsecme-chacha20-poly1305-01
// http://tools.ietf.org/html/draft-mavrogiannopoulos-chacha-tls-02
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_SHA",
"TLS_ECDHE_RSA_WITH_CHACHA20_SHA",
"TLS_DHE_RSA_WITH_CHACHA20_POLY1305",
"TLS_RSA_WITH_CHACHA20_POLY1305",
"TLS_DHE_RSA_WITH_CHACHA20_SHA",
"TLS_RSA_WITH_CHACHA20_SHA",
// Done with bleeding edge, back to TLS v1.2 and below
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
// TLS v1.0 (with some SSLv3 interop)
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
"SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA",
// RSA key transport sucks, but they are needed as a fallback.
// For example, microsoft.com fails under all versions of TLS
// if they are not included. If only TLS 1.0 is available at
// the client, then google.com will fail too. TLS v1.3 is
// trying to deprecate them, so it will be interesteng to see
// what happens.
"TLS_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA"
};
String[] availableCiphers = null;
try
{
SSLSocketFactory factory = m_ctx.getSocketFactory();
availableCiphers = factory.getSupportedCipherSuites();
Arrays.sort(availableCiphers);
}
catch(Exception e)
{
return new String[] {
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
};
}
List<String> aa = new ArrayList<String>();
for(int i = 0; i < preferredCiphers.length; i++)
{
int idx = Arrays.binarySearch(availableCiphers, preferredCiphers[i]);
if(idx >= 0)
aa.add(preferredCiphers[i]);
}
aa.add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV");
return aa.toArray(new String[0]);
}
private SSLContext m_ctx;
private String[] m_ciphers;
private String[] m_protocols;
}
'TLS_RSA_WITH_AES_256_CBC_SHA' ... - probablemente debería evitar los sistemas de transporte de claves RSA (o colocarlos en la parte inferior de la lista anunciada) En su lugar, favorezca los intercambios de claves efímeras, como 'DHE', para mantener el secreto hacia adelante. De hecho, TLS 1.3 está discutiendo su eliminación porque carecen de la propiedad. – jww
'SSL_RSA_WITH_RC4_128_MD5' ... -' RC4' es el hijo problemático ahora. Ver [Sobre la seguridad de RC4 en TLS y WPA] (http://www.isg.rhul.ac.uk/tls/). Un atacante probablemente * no puede * falsificar una firma 'HMAC-MD5' en la ventana de tiempo 2MSL de la red. Sin embargo, un atacante puede correlacionar estadísticamente bits en la secuencia de cifrado. (Y MD5 * está * muerto para uso a largo plazo, como certificados y firmas digitales). – jww
'SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA' ... - grabarlo. Evítalo como la peste. – jww