Java 7 и не удалось создать DH keypair

Я прочитал предыдущее сообщение об ошибке "Не удалось создать DH keypair", когда сервер отправляет ключ длиннее 1024 бит. Загрузка JCE неограниченных банок должна исправить эту проблему. В тестовой среде я столкнулся с следующим: для того же веб-сервера, если я использую Java 6, я не получаю никаких ошибок при выполнении запроса https, но если я использую Java 7, тогда я получаю "Не удалось создать DH keypair".

Я попытался заменить файлы jar для JCE неограниченным, но все равно получить ту же ошибку. Об ошибке сообщается с 2007 года, но почему она запускается для Java 6, а не для Java 7? Не загружаются ли файлы не соответствующими? Я получил ссылку с предыдущего сообщения Java: почему SSL-квитирование дает исключение "Не удалось создать DH keypair" ?.

В этот момент я не знаю, что делать. Если я попытаюсь загрузить поставщика BouncyCastle, я получаю исключение ArrayOutOfIndex. Мой сервер разрешает только алгоритм DH, поэтому я не могу использовать другой алгоритм, как предлагается в вышеприведенном сообщении.

Ответы

Ответ 1

Некоторые дополнения или пояснения:

(Suncle) Java 7, поскольку 7u09 использует более разумный последовательный порядок шифров по умолчанию, в отличие от кажущегося случайного порядка в 7u04. (У меня нет тестов с 04 по 09.) Этот заказ помещает ECDHE и plain-RSA (aka akRSA) перед DHE и, таким образом, позволяет избежать проблемы, если И ТОЛЬКО ЕСЛИ сервер поддерживает ECDHE или RSA и соглашается с предпочтением клиента. (Или ECDH-исправлено, но практически никто не использует это.) Если сервер настаивает на DHE (по какой-либо причине) И использует DH > 1024 бит, у вас все еще есть проблема.

Если искатель (или кто-либо еще) подключается к серверу, которому действительно требуется integer-DH (а не ECDH или RSA), единственный способ работать с Java до 8 - заставить сервер использовать DH 1024-бит. Какой AFAWK технически безопасен еще несколько лет, но с небольшим отрывом ему запрещают важные власти, такие как NIST (см. Специальный паб 800-57 на csrc.nist.gov). (Даже RSA 1024 на самом деле еще не сломан, но, вероятно, это будет скоро, и поэтому это запрещено.)

"Политика неограниченной прочности" не имеет отношения к этой проблеме или, по крайней мере, не напрямую, и хорошие ответы на # 6851461 не говорят об этом. Он не изменяет ограничение параметров DH в SunJCE, которое (ошибочно) рассматривается как стандартная проблема, а не проблема с сильной стороной. (В частности, он принимает ограничения, которые были правильны для DSA, и применяет их к DH.) Он включает в себя пакеты AES-256 и SHA-2 (только для TLSv1.2) и дает достаточно странный список предпочтений, который может изменить результат выбора из DHE (сбой) на не-DHE (работает).

Вам не нужно полностью возвращаться к списку Java 6, вам просто нужно назначить приоритет другим обмену ключами через DHE или полностью отказываться от DHE сервера DHE. Вы определенно НЕ должны возвращаться к разрешению любых EXPORT или наборов DES-DES, если это абсолютно необходимо для старого сервера; они не были БЕЗОПАСНЫ в течение нескольких лет, и по-прежнему оставались включенными по умолчанию в 6 дольше, чем они должны были.

Ответ 2

Я наткнулся на ту же проблему с SSLScokets, и я думаю, что я определил причину этой регрессии с Java 7. Причина кроется в шифрах, согласованных между клиентом и сервером.

По умолчанию Java 6 включает эти шифры для TLS-соединения (в порядке приоритета):

SSL_RSA_WITH_RC4_128_MD5
SSL_RSA_WITH_RC4_128_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
SSL_RSA_WITH_3DES_EDE_CBC_SHA
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_DES_CBC_SHA
SSL_DHE_RSA_WITH_DES_CBC_SHA
SSL_DHE_DSS_WITH_DES_CBC_SHA
SSL_RSA_EXPORT_WITH_RC4_40_MD5
SSL_RSA_EXPORT_WITH_DES40_CBC_SHA
SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
TLS_EMPTY_RENEGOTIATION_INFO_SCSV

И Java 7 позволяет использовать эти шифры:

TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_RC4_128_SHA
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDHE_RSA_WITH_RC4_128_SHA
TLS_ECDH_ECDSA_WITH_RC4_128_SHA
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_RSA_WITH_RC4_128_SHA
TLS_EMPTY_RENEGOTIATION_INFO_SCSV
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_RC4_128_MD5
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_3DES_EDE_CBC_SHA

Шифры, использующие Diffie-Hellman, имеют более высокий приоритет на Java 7, но они, похоже, не поддерживают ключи длиннее 1024 бит, если не установлен сильный криптова пакет.

Обходной путь, который я использовал, заключался в том, чтобы указать шифры, разрешенные Java 6, на SSLSocket:

SSLSocketFactory socketFactory = SSLContext.getInstance("TLS").getSocketFactory();
SSLSocket socket = (SSLSocket) socketFactory.createSocket(InetAddress.getByName(hostname), port);
socket.setEnabledCipherSuites(new String[] {
        "SSL_RSA_WITH_RC4_128_MD5",
        "SSL_RSA_WITH_RC4_128_SHA",
        "TLS_RSA_WITH_AES_128_CBC_SHA",
        "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
        "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
        "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
        "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
        "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
        "SSL_RSA_WITH_DES_CBC_SHA",
        "SSL_DHE_RSA_WITH_DES_CBC_SHA",
        "SSL_DHE_DSS_WITH_DES_CBC_SHA",
        "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
        "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
        "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
        "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
        "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"});

socket.startHandshake();

Ответ 3

Учитывая, что вы используете последнее издание java и все еще получаете ошибку, вы можете изменить параметр в java.security(например, в папке C:\Program Files\Java\jre1.8.0_xx\lib\security

# Example:
#   jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048
    jdk.tls.disabledAlgorithms=SSLv3, RC4

Добавить DH как отключенный алгоритм в jdk.tls.disabledAlgorithms

    jdk.tls.disabledAlgorithms=SSLv3, RC4, DH

Перезапустите tomcat или запустите программу.

Ответ 4

Мы также столкнулись с этой проблемой с Java7 и Java8. Мы также использовали обходное решение, подобное предложениям Emanual Borg. Но наша цель состояла в том, чтобы избежать жесткого кодирования фиксированного списка CipherSuites. Поэтому мы попытались удалить записи, вызвавшие проблему (методом проб и ошибок...).

String[] enabledCipherSuites = socket.getEnabledCipherSuites();

// avoid hardcoding a new list, we just remove the entries
// which cause the exception
List<String> asList = new ArrayList(Arrays.asList(enabledCipherSuites));

// we identified the following entries causeing the problems
// "Could not generate DH keypair"
// and "Caused by: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)"
asList.remove("TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
asList.remove("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA");
asList.remove("TLS_DHE_RSA_WITH_AES_256_CBC_SHA");

String[] array = asList.toArray(new String[0]);
socket.setEnabledCipherSuites(array);

Вопрос: Кто-нибудь видит проблему с этим подходом?

Btw: Если вы используете Apache HTTPClient, то https://issues.apache.org/jira/browse/HTTPCLIENT-1111 интересен, что показывает, как установить CipherSuites (начиная с HTTPClient v4.2) с помощью метода

SSLConnectionSocketFactory() {...}.prepareSocket(SSLSocket)

Обновление 2015/10/31: Чтобы лучше понять контекст, где это можно использовать, вот как полный пример псевдокода, где вы видите, как подключаться к методу prepareSocket():

HttpClientBuilder builder = HttpClients.custom();

SSLContextBuilder sslContextBuilder = SSLContexts.custom();
SSLContext sslContext = sslContextBuilder.build();

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostNameVerfier)
{


    protected void prepareSocket(SSLSocket socket) throws IOException {

    // Workaround to use different order of CipherSuites used by Java6 in order
        // to avoid the the problem of java7 "Could not generate DH keypair"
        String[] enabledCipherSuites = socket.getEnabledCipherSuites();

        // but to avoid hardcoding a new list, we just remove the entries
        // which cause the exception (via TrialAndError)
        List<String> asList = new ArrayList(Arrays.asList(enabledCipherSuites));

        // we identified the following entries causeing the problems
        // "Could not generate DH keypair"
        // and "Caused by: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)"
        asList.remove("TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
        asList.remove("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA");
        asList.remove("TLS_DHE_RSA_WITH_AES_256_CBC_SHA");

        String[] array = asList.toArray(new String[0]);
        socket.setEnabledCipherSuites(array);

    };
};

Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create().register("https", sslsf).build();

PoolingHttpClientConnectionManager conman = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
builder.setConnectionManager(conman);

CloseableHttpClient httpClient =  builder.build();

Будьте осторожны Мы используем этот фрагмент кода только в контексте, в котором пользователь явно позволяет доверять самоподписанным сертификатам (например, для тестовых сред и т.д.). Если вы не хотите этого делать, тогда лучше не вмешиваться в SSL-материал.

Ответ 5

Если вы используете jdk1.7.0_04, перейдите на jdk1.7.0_21. Проблема была исправлена ​​в этом обновлении.