Определить длину "параметров" Диффи-Хеллмана для рукопожатия 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
с этой точки зрения. "Настоящий" сеанс SSLS существует и дает мне информацию о соединении, но "сеанс рукопожатия", кажется, всегда null
,
Поэтому я попытался написать HandshakeCompletedListener
и я на самом деле получить SSLSession
, но, похоже, это тот же самый, который я могу получить от SSLSocket
уже, так что сессия "рукопожатия" кажется бесполезной.
Как я могу получить эти параметры из SSLSession
?
2 ответа
Параметры этих параметров встроены в набор шифров (то есть они всегда 1024-разрядные) или это конфигурация сервера, которая делает эти наборы шифров слабыми из-за выбора конкретного параметра DH?
Нет, это параметр конфигурации для протокола. Для Java по умолчанию установлено значение 1024 бита, но его можно изменить глобально для JSSE (реализация Java TLS), используя системное свойство: jdk.tls.ephemeralDHKeySize
, Лучше всего устанавливать это при запуске с параметром -D для Java VM.
Для статических пар ключей DH (которые используются для аутентификации) вы должны изучить сертификат DH. Но я не думаю, что вы найдете что-либо, все используют RSA для аутентификации.
В любом случае я хотел бы иметь возможность прослушивать эту информацию из соединения, если это вообще возможно. Кто-нибудь знает, можно ли это сделать и как?
Ну, для прослушивания инструментов, таких как WireShark будет достаточно. Несомненно, вы можете анализировать такие вещи, как параметры DH, из соединения TLS (если они, конечно, используются в первую очередь).
Вы также можете отлаживать соединения, используя -Djavax.net.debug
Для приложений / библиотек Java вы можете найти комплект шифров, а затем, если он содержит DHE_
посмотрите вышеупомянутое системное свойство (имея в виду его значения по умолчанию).
Java JSSE API не был написан с глубокой проверкой пакетов. Это (буквально) сервис-ориентированная реализация для серверов и клиентских приложений. Хотя вы, конечно, можете использовать сам код OpenJDK (он написан на GPL, верно?), Вам лучше использовать отдельную реализацию, возможно, с еще более разрешительной лицензией.
Однако для сниффера я бы предпочел использовать C/C++ (или, по крайней мере, внешний интерфейс C / C++), а не Java.
Для большинства алгоритмов шифрования длина определяется именем шифра имени, как также упоминалось здесь. Как получить фактический размер ключа блочного шифра для соединения Java SSL _ в коде_?, Вместо того чтобы пытаться предупредить людей, когда они используют незащищенные шифры, я бы порекомендовал отключить эти шифры, выбрав только те шифры, которые вы хотите поддерживать. Вы можете сделать это на уровне 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
Вы можете увидеть настройки по умолчанию и классы, используемые в безопасности Java, здесь https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html
Если вам интересно, и вы хотите исследовать это более подробно, я бы рекомендовал включить протоколирование ssl, как описано здесь.