Отладка исключения SSLSocket.startHandshake - PullRequest
1 голос
/ 04 февраля 2020

У меня есть клиентские / серверные приложения на основе java TLS, которые я писал 2-3 года go и с тех пор почти ежедневно использую на различных установках linux. Я не делаю много работы в java - писать это было действительно упражнением.

Во всяком случае, это никогда не вызывало у меня горя с тех пор, до вчерашнего дня, когда я вдруг не могу пройти аутентификацию на сервере из коробки Fedora 31. Причиной было обновление openjdk накануне. Другие машины, на которых не было обновления, не имеют проблемы. Тем не менее, я попытался восстановить с помощью Oracle JDK 13, и проблема была воспроизведена.

Просто чтобы прояснить:

  • Сервер, на котором выброшено исключение, не обновлено.
  • Единственные клиенты, которые в настоящее время терпят неудачу, это клиенты, подключающиеся из системы с обновлением jdk.
  • Компиляция всего приложения и тестирование с недавним Oracle JDK имеет проблема - клиенты не могут подключиться к серверам. Так что это то, что я должен исправить или откатить JRE и использовать это везде. > _ <</li>

Аутентификация выполняется с использованием PKIX, где имеется частный ЦС, поддерживающий набор сертификатов клиента и сервера, в файлах pkcs12, созданных с помощью keytool. SSLContext инициализируется с TrustManagerFactor и KeyStore более или менее так:

KeyStore trusts;                                       
TrustManagerFactory tmf;                               
String trustPassword("abc123");                         

try (                                                  
    FileInputStream trustIn =                          
        new FileInputStream(new File("foobar.pkcs12")) 
) {                                                    
    tmf = TrustManagerFactory.getInstance("PKIX");     
    trusts = KeyStore.getInstance("pkcs12");           
    trusts.load(trustIn, trustPassword.toCharArray()); 
    tmf.init(trusts);                                  
}                                                      

SSLContext sscon = SSLContext.getInstance("TLSv1.2");  
sscon.init (                                           
    someKeyManager,                                    
    tmf.getTrustManagers(),                            
    new SecureRandom()                                 
);

Как уже упоминалось, я не волшебник java. Тем не менее, я занимался сетевым программированием TLS на других языках, поэтому концепции довольно знакомы.

Создается сокет сервера:

SSLServerSocket listenSock = sscon
    .getServerSocketFactory()
    .createServerSocket(port, backlog);  

И клиентское соединение:

SSLSocket client = (SSLSocket)listenSock.accept(); 
SSLParameters sslp = new SSLParameters();       
sslp.setProtocols(new String[]{"TLSv1.2"}); 
client.setSSLParameters(sslp);              

try { client.startHandshake(); }

То, что try - это то место, где теперь на сервере выдается исключение, «Получено фатальное оповещение: сертификат_ответ» .

A jdb dump исключения:

ex = {
    serialVersionUID: 4511006460650708967
    java.io.IOException.serialVersionUID: 7818375828146090155
    java.lang.Exception.serialVersionUID: -3387516993124229948
    java.lang.Throwable.serialVersionUID: -3042686055658047285
    java.lang.Throwable.backtrace: instance of java.lang.Object[5] (id=2005)
    java.lang.Throwable.detailMessage: "readHandshakeRecord"
    java.lang.Throwable.UNASSIGNED_STACK: instance of java.lang.StackTraceElement[0] (id=2007)
    java.lang.Throwable.cause: instance of java.net.SocketException(id=2008)
    java.lang.Throwable.stackTrace: instance of java.lang.StackTraceElement[0] (id=2007)
    java.lang.Throwable.depth: 5
    java.lang.Throwable.SUPPRESSED_SENTINEL: instance of java.util.Collections$EmptyList(id=2009)
    java.lang.Throwable.suppressedExceptions: instance of java.util.ArrayList(id=2010)
    java.lang.Throwable.NULL_CAUSE_MESSAGE: "Cannot suppress a null exception."
    java.lang.Throwable.SELF_SUPPRESSION_MESSAGE: "Self-suppression not permitted"
    java.lang.Throwable.CAUSE_CAPTION: "Caused by: "
    java.lang.Throwable.SUPPRESSED_CAPTION: "Suppressed: "
    java.lang.Throwable.EMPTY_THROWABLE_ARRAY: instance of java.lang.Throwable[0] (id=2015)
    java.lang.Throwable.$assertionsDisabled: true
}

Ничто из этого не полезно для меня, то есть я не понимать это.

Откуда я go отсюда? Сертификаты в порядке, по крайней мере, в соответствии с keytool, и, как уже упоминалось, этот код работал в течение многих лет, часто обращался без проблем. 1 Я искал вокруг о специфике * Ошибка 1064 * («сертификат неизвестен»), но результаты не очень полезны - сертификат есть, сертификат не просрочен, доверие было загружено изначально без спецификации, и т. Д. c.


  1. Это включает в себя регенерацию материала PKIX с keytool через регулярные промежутки времени, поскольку сертификаты создаются с ограниченным сроком службы. Пары ключей используют 2048 бит RSA и -sigalg SHA256withRSA.

1 Ответ

0 голосов
/ 05 февраля 2020

Как оказалось, вы можете получить замечательные выходные данные отладки во время рукопожатия, используя:

java --Djavax.net.debug=all ...

Подробнее об этом: https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/ReadDebug.html

Так как только клиентская система имела проблему после обновления, имеет смысл, что именно клиент отклонил соединение. И причина:

TrustAnchor with subject "CN=myAuthority, C=CA"
does not have keyCertSign bit set in KeyUsage extension

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

Конечная причина была в сценарии, который я использовал все это время для регенерации сертификатов, и который изначально использовался для создания ЦС. Это часть параметров, передаваемых keytool при создании самозаверяющего сертификата CA:

-ext KU=keyCertSign -ext KU=cRLSign

Сертификат имел последний, что означает, что предыдущее значение было заменено. Правильный способ сделать это состоит в том, чтобы перечислить их вместе:

-ext KU=keyCertSign,cRLSign

Замена всего дефектного в инфраструктуре root PKIX не была слишком сложной после всего этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...