Определите длину параметров Диффи-Хеллмана для рукопожатия TLS в Java

Я хотел бы установить HTTPS-соединение с сервером, и если я использую не эфемерный обмен ключами DH, я хотел бы знать, каковы параметры для этого соединения. На самом деле, мне все равно, эфемерно это или нет.

Я ищу возможность установить соединение, а затем предупредить, если соединение использует «слабые» параметры DH. Это то, что я могу проверить во время подключения? Или набор параметров DH (или, точнее, длина этих параметров в битах) определяется самим набором шифров?

Например, в ветке сообщества Qualys есть иллюстрация наборов шифров, которые SSLLabs считает «слабыми» (ну, все считают их слабыми... у них просто есть общедоступный инструмент, который жалуется на них): https://community.qualys.com/thread/14821

Они конкретно упоминают, например. TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, который представляет собой набор шифров 0x9f, и укажите параметры DH. Параметры этих параметров встроены в набор шифров (то есть они всегда 1024-битные) или это конфигурация сервера, которая делает эти наборы шифров слабыми из-за выбора конкретного параметра DH?

В любом случае, я хотел бы иметь возможность прослушивать эту информацию из соединения, если это вообще возможно. Кто-нибудь знает, можно ли это сделать и как?

Я написал некоторый код, чтобы попытаться получить эту информацию о рукопожатии, но я продолжаю получать null для объекта, который, как я надеялся, будет содержать эти данные.

SSLSocketFactory sf = ...;
Socket sock = new Socket();
sock.connect(address, timeout);

SSLSocket socket = (SSLSocket)sf.createSocket(sock, host, port, true);
socket.startHandshake();
SSLSession sess = socket.getHandshakeSession();

Я надеялся, что sess в этот момент будет содержать какую-то интересную информацию о рукопожатии, но это null. В javadoc для startHandshake указано, что он уведомит прослушиватель событий о завершении рукопожатия. Итак, я попробовал это:

SSLSocketFactory sf = ...;
Socket sock = new Socket();
sock.connect(address, timeout);

SSLSocket socket = (SSLSocket)sf.createSocket(sock, host, port, true);
socket.startHandshake();
// SSLSession sess = socket.getHandshakeSession();
SSLSession sess = socket.getSession(); // This forces the handshake to complete
sess = socket.getHandshakeSession();

... но sess все еще null на данный момент. «Настоящая» SSLSession действительно существует и дает мне информацию о соединении, но «сеанс рукопожатия», кажется, всегда null.

Итак, я попытался написать HandshakeCompletedListener, и на самом деле я получил SSLSession, но похоже, что это то же самое, что я уже могу получить от SSLSocket, поэтому сеанс "рукопожатия" кажется бесполезным.

Как я могу получить эти параметры из SSLSession?


person Christopher Schultz    schedule 07.09.2016    source источник
comment
.getHandshakeSession() работает только во время рукопожатия; после этого результаты находятся в .getSession(), как вы нашли. SSLSession содержит сертификат однорангового узла и, таким образом, содержит или будет содержать ключ static-DH, который, как говорит Маартен, никто не использует; эфемерный ключ DH по определению не сертифицирован и не сохраняется в файле SSLSession.   -  person dave_thompson_085    schedule 03.01.2017
comment
Вместо предупреждения вы можете заставить JSSE сбой установить соединение со слишком маленьким DH, RSA или DSA со свойством безопасности jdk.tls.disabledAlgorithms, установленным в коде, или JRE/lib/security/java.security q.v. для док. Это (намного?!) менее удобно для пользователей, конечно.   -  person dave_thompson_085    schedule 03.01.2017
comment
@ dave_thompson_085 Я не верю, что для эфемерных ключей можно установить какие-либо пороговые значения. Все, что можно сделать, это установить (один и единственный) размер эфемерного ключа для всей JVM. Я не пытался увидеть, что произойдет, если вы попытаетесь изменить размер эфемерного ключа после закрытия одного соединения и открытия другого. Может быть возможно обнаружить, что сервер разрешает использование слабого эфемерного размера ключа Диффи-Хелмана (например, 768 бит), но не представляется возможным определить размер ключа, используемый в настоящее время (кроме процесса исключения).   -  person Christopher Schultz    schedule 03.01.2017
comment
«DH keySize‹1024», например, применим к DHE для меня в текущей версии Java, хотя он не работает в некоторых старых версиях, которые мне все еще нужно иметь; отсечка, по-видимому, составляет 8u51 или около того. Изменение внутри процесса действительно может быть проблемой; мои тестовые программы не используют это, и я видел другую конфигурацию «замораживания» кода JDK, например. Хранилище доверенных сертификатов JSSE по умолчанию нельзя изменить после первого использования.   -  person dave_thompson_085    schedule 03.01.2017


Ответы (2)


Параметры этих параметров встроены в набор шифров (то есть они всегда 1024-битные) или это конфигурация сервера, которая делает эти наборы шифров слабыми из-за выбора конкретного параметра DH?

Нет, это параметр конфигурации для протокола. По умолчанию для Java используется 1024 бита, но его можно изменить глобально для JSSE (реализация Java TLS) с помощью системного свойства: jdk.tls.ephemeralDHKeySize. Лучше всего установить это во время запуска с параметром -D для виртуальной машины Java.

Для статических пар ключей Диффи-Хелмана (используемых для аутентификации) вам нужно будет изучить сертификат Диффи-Хелмана. Но я не думаю, что вы найдете их, все используют RSA для аутентификации.

В любом случае, я хотел бы иметь возможность прослушивать эту информацию из соединения, если это вообще возможно. Кто-нибудь знает, можно ли это сделать и как?

Что ж, для обнюхивания таких инструментов, как WireShark, будет достаточно. Несомненно, вы можете анализировать такие вещи, как параметры DH из соединения TLS (если они используются в первую очередь, конечно).

Вы также можете отлаживать подключения, используя -Djavax.net.debug

Для приложений / библиотек Java вы можете найти набор шифров, а затем, если он содержит DHE_, найти вышеупомянутое системное свойство (с учетом его значений по умолчанию).


Java JSSE API не был написан с учетом глубокой проверки пакетов. Это (буквально) сервис-ориентированная реализация для серверов и клиентских приложений. Хотя вы, конечно, можете использовать и сам код OpenJDK (он под лицензией GPL, верно?), вам лучше использовать отдельную реализацию, возможно, с еще более разрешительной лицензией.

Однако для сниффера я бы предпочел использовать C/C++ (или, по крайней мере, интерфейс C/C++), а не Java.

person Maarten Bodewes    schedule 03.01.2017
comment
На самом деле я пытаюсь написать инструмент для прослушивания, а не использовать его. :) Поэтому мне было интересно, предоставляет ли Java API какой-либо способ получить эти значения из установленного (или текущего) соединения. Я думаю, что использование -Djavax.net.debug показывает информацию, которую я хотел бы (я опубликовал это некоторое время назад и не возвращался в последнее время, чтобы исследовать), но синтаксический анализ stdout кажется ужасным способом получить информацию из API. - person Christopher Schultz; 03.01.2017
comment
Использование JSSE для реализации сниффера кажется ужасным способом получения информации из соединения TLS. - person Maarten Bodewes; 03.01.2017
comment
Код Java сам устанавливает соединение. Я просто подумал, что было бы разумно получить эти сведения о соединении, если я также могу получить сведения о том, какой набор шифров используется и т. д. Использование внешнего анализатора для определения того, что происходит с соединением, установленным мой код кажется глупым, если я могу просто изменить код, чтобы получить эту информацию. Я ищу что-то, что я мог пропустить в API Java, чтобы предоставить эту информацию из установленного соединения, над которым я полностью контролирую. Я не пытаюсь реализовать Wireshark на Java. - person Christopher Schultz; 03.01.2017
comment
Обратите внимание, что параметр ephemeralDHKeySize предназначен только для JSSE (Java) сервера; он игнорируется для клиента, и этот вопрос, похоже, касается клиента. - person dave_thompson_085; 03.01.2017
comment
Хм, интересная штука. Я обязательно буду внимательно следить за тем, как это происходит, хотя бы для того, чтобы помочь в возможной реализации TLS 1.3 (или как там это будет называться). - person Maarten Bodewes; 03.01.2017

Для большинства алгоритмов шифрования длина определяется именем шифра имени, как также упоминается здесь key-size-for-java-ssl-connection-in-code">Как получить фактический размер ключа блочного шифра для соединения Java SSL _in code_? . Вместо того, чтобы пытаться предупредить людей, когда они используют небезопасные шифры, я бы рекомендовал отключить эти шифры, выбрав только те шифры, которые вы хотите поддерживать. Вы можете сделать это на уровне jvm или на SSLSocket, например.

String pickedCipher[] ={"TLS_RSA_WITH_AES_128_CBC_SHA"}; 
socket.setEnabledCipherSuites(pickedCipher);

Вы также можете установить желаемый размер ключа, см. здесь https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#customizing_dh_keys

Вы можете увидеть значения по умолчанию и классы, используемые в безопасности Java, здесь https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html

Если вам любопытно и вы хотите изучить это более подробно, я бы порекомендовал включить ведение журнала ssl, как описано здесь.

person Guenther    schedule 16.09.2016
comment
Размер ключа шифра data определяется набором шифров; размеры (и значения) ключей DH, ECDH и RSA/DSA/ECDSA, используемых для обмена ключами и аутентификации, не совпадают, за исключением старых устаревших слабых и сломанных наборов «экспорта», которые никто не должен использовать или разрешать сегодня, верхний предел размера DHE или kRSA. Сервер может настраивать размеры ключа Диффи-Хелмана, но Q, по-видимому, предназначен для клиента. - person dave_thompson_085; 03.01.2017