Java 7 и не удалось сгенерировать пару ключей DH

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

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

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


person user1408089    schedule 21.05.2012    source источник


Ответы (5)


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

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

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

«Политика неограниченной силы» не имеет отношения к этой проблеме или, по крайней мере, не имеет прямого отношения, и хорошие ответы на # 6851461 не говорят об этом. Это не меняет ограничение на параметры DH в SunJCE, которое (ошибочно) рассматривается как стандартная проблема, а не проблема надежности. (В частности, он берет ограничения, которые раньше были правильными для DSA, и применяет их к DH). изменить результат выбора с DHE (сбой) на не-DHE (работает).

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

person dave_thompson_085    schedule 07.02.2014

Я столкнулся с той же проблемой с 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();
person Emmanuel Bourg    schedule 27.07.2012
comment
Большое спасибо! Я столкнулся со странной ошибкой java.security.ProviderException: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_DOMAIN_PARAMS_INVALID при использовании Webscarab (SSL MITM-прокси). Явное указание этих наборов шифров заставляет его снова работать. - person Lekensteyn; 17.01.2013
comment
Я новичок в ценных бумагах Java. Где я должен написать этот кусок кода? - person Shashank; 05.10.2015

Если вы используете последнюю версию 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 или перезапустите программу.

person Tolomir    schedule 03.11.2015

Мы также столкнулись с этой проблемой с Java7 и Java8. Мы также использовали обходной путь, аналогичный предложениям Эмануала Борга. Но нашей целью было избежать жесткого кодирования фиксированного списка 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);

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

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

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

Обновление от 31 октября 2015 г. Чтобы лучше понять контекст, в котором это можно использовать, приведем полный пример псевдокода, в котором показано, как подключиться для переопределения метода 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-вещами.

person Christoph    schedule 02.09.2015
comment
Я использую apache httpclient, но не могу понять, как использовать prepareSocket - person coding_idiot; 30.10.2015

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

person Lekkie    schedule 22.05.2013
comment
Прохладный. Это было исправлено в новых версиях Java. Но мой вопрос касается использования более старой версии. Когда я использую более старую версию, иногда она работает, а иногда выдает вышеуказанное исключение. Почему такое случайное поведение? Если это ошибка в java, то я думаю, она никогда не должна работать? - person N..; 26.08.2013
comment
К сожалению, я все еще получаю эту ошибку в 7u21-2.3.9-1ubuntu1. - person expert; 01.10.2013
comment
И я понимаю это в 1.7.0_55-b13. - person Chris W.; 17.10.2014
comment
У меня была такая же проблема с jdk 7 в amazon linux, обновление до oracle jdk8 решило проблему. - person user884424; 07.11.2014