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. Проблема была исправлена в этом обновлении.