Какие 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
        }
    }
Другие вопросы по тегам