Какие Cipher Suites включить для SSL Socket?
Я использую SSLSocket Java для защиты связи между клиентом и серверной программой. Программа сервера также обслуживает запросы HTTPS от веб-браузеров.
В соответствии с разделом " Начало криптографии с Java ", стр. 371, вы всегда должны звонить setEnabledCipherSuites
на ваше SSLSocket
/ SSLServerSocket
чтобы убедиться, что набор шифров, который в итоге согласовывается, достаточно силен для ваших целей.
Как говорится, звонок в мой SSLSocketFactory
"s getDefaultCipherSuites
Метод дает около 180 вариантов. Эти варианты варьируются от TLS_RSA_WITH_AES_256_CBC_SHA
(который я считаю довольно безопасным) SSL_RSA_WITH_RC4_128_MD5
(не уверен, что это безопасно, учитывая текущий статус MD5) SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
(не совсем уверен, что это делает).
Что такое разумный список наборов шифров для ограничения сокетов?
Обратите внимание, что клиент и сервер имеют доступ к поставщику услуг Bouncy Castle, и что они могут иметь или не иметь неограниченные файлы криптографической политики.
3 ответа
Не используйте ничего с экспортом в нем. Это из-за ограничений экспорта из-за сильной криптографии.
РЕДАКТИРОВАТЬ: Изменено для использования документа 2009 года.
В рекомендации NIST за 2009 год перечислено следующее, включая TLS_RSA_WITH_AES_256_CBC_SHA (о котором вы упомянули):
TLS_RSA_WITH_NULL_SHA (не используйте это, если вы не уверены, что вам не нужна конфиденциальность / конфиденциальность).
TLS_RSA_WITH_3DES_EDE_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
TLS_DH_DSS_WITH_AES_128_CBC_SHA
TLS_DH_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DH_DSS_WITH_AES_256_CBC_SHA
TLS_DH_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_DSS_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_PSK_WITH_3DES_EDE_CBC_SHA
TLS_PSK_WITH_AES_128_CBC_SHA
TLS_PSK_WITH_AES_256_CBC_SHA
TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA
TLS_DHE_PSK_WITH_AES_128_CBC_SHA
TLS_DHE_PSK_WITH_AES_256_CBC_SHA
TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA
TLS_RSA_PSK_WITH_AES_128_CBC_SHA
TLS_RSA_PSK_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ниже приведен класс Java, который я использую для реализации комплектов шифрования и протоколов. До SSLSocketFactoryEx
Я изменял свойства на SSLSocket
когда у меня был доступ к ним. В этом помогали Java-пользователи из Stack Overflow, так что приятно размещать его здесь.
SSLSocketFactoryEx
предпочитает более сильные комплекты шифров ECDHE
а также DHE
), и он пропускает слабые и раненые наборы шифров (например, RC4
а также MD5
). Для взаимодействия с Google и Microsoft необходимо включить четыре ключевых шифра RSA, когда TLS 1.2 недоступен. Они есть TLS_RSA_WITH_AES_256_CBC_SHA256
,TLS_RSA_WITH_AES_256_CBC_SHA
и двое друзей. Если возможно, вы должны удалить TLS_RSA_*
Ключевые транспортные схемы.
Держите список наборов шифров как можно меньше. Если вы рекламируетевсе доступные шифры (аналогично списку Flaschen), то ваш список будет более 80. Это занимает 160 байтов в ClientHello
и это может вызвать сбой некоторых устройств, поскольку они имеют небольшой буфер фиксированного размера для обработкиClientHello
, Сломанные приборы включают F5 и Ironport.
На практике список в приведенном ниже коде соединяется до 10 или 15 наборов шифров, как только предпочтительный список пересекается с поддерживаемыми наборами шифров Java. Например, вот список, который я получаю при подготовке к подключению, или microsoft.com, или google.com с действующей неограниченной политикой JCE:
- 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_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
В списке отсутствуют слабые / раненые алгоритмы, такие как RC4 и MD5. Если они включены, то вы, скорее всего, получите предупреждение об устаревшей криптографии из браузера.
Список будет меньше с политикой JCE по умолчанию, потому что политика удаляет AES-256 и некоторые другие. Я думаю, что это около 7 комплектов шифров с ограниченной политикой.
SSLSocketFactoryEx
Класс также обеспечивает использование протоколов TLS 1.0 и выше. Java-клиенты до Java 8 отключают TLS 1.1 и 1.2. SSLContext.getInstance("TLS")
также будет пробираться SSLv3
(даже в Java 8), поэтому необходимо предпринять шаги для его удаления.
Наконец, приведенный ниже класс поддерживает TLS 1.3, поэтому он должен работать, когда поставщик делает их доступными. *_CHACHA20_POLY1305
комплекты шифров предпочтительнее, если они доступны, потому что они намного быстрее, чем некоторые из существующих комплектов, и имеют лучшие свойства безопасности. Google уже развернул его на своих серверах. Я не уверен, когда Oracle предоставит их. OpenSSL предоставит им OpenSSL 1.0.2 1.1.0.
Вы можете использовать это так:
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;
}
Здесь вы можете проверить, насколько безопасен шифр: https://ciphersuite.info/cs/
Вот способ проверки шифра через API:
public static boolean isCipherSecure(String cipher) throws Exception
{
URL url = new URL("https://ciphersuite.info/api/cs/");
URLConnection request = url.openConnection();
request.connect();
JsonElement root = JsonParser.parseReader(new InputStreamReader((InputStream) request.getContent()));
JsonObject rootobj = root.getAsJsonObject();
JsonArray suits = rootobj.getAsJsonArray("ciphersuites");
for (JsonElement el : suits)
{
Set<Map.Entry<String, JsonElement>> entries = el.getAsJsonObject().entrySet();
for (Map.Entry<String, JsonElement> entry : entries)
{
if (entry.getKey().contentEquals(cipher) || entry.getValue().getAsJsonObject()
.getAsJsonPrimitive("openssl_name").getAsString().contentEquals(cipher))
{
String sec = entry.getValue().getAsJsonObject().getAsJsonPrimitive("security").getAsString();
System.out.println(entry.getKey() + ": " + sec);
if (sec.contentEquals("recommended") || sec.contentEquals("secure"))
{
return true;
}
else
return false;
}
}
}
throw new Exception("Can't find cipher: " + cipher);
}
Звонок:
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
for (String cipher : ssf.getSupportedCipherSuites())
{
if (isCipherSecure(cipher))
{
//your code
}
}