Сокет SSL-сервера хочет вариант аутентификации

Относительно SSLServerSocket. setWantClientAuth:
Если установлено значение true, если клиент решает не отправлять сертификат, согласование продолжается.
Также я заметил, что это также происходит, если клиент отправляет сертификат, но не является частью хранилища доверенных сертификатов. В этом случае согласование также не завершается ошибкой.

Итак, каков вариант использования этой настройки?


person Jim    schedule 14.02.2013    source источник
comment
Бруно: Я пробовал как Java, так и не-Java-клиенты.   -  person Jim    schedule 16.02.2013


Ответы (2)


(Несколько правок после ряда комментариев.)

setWantClientAuth используется для запроса аутентификации сертификата клиента, но сохраняет соединение, если аутентификация не предоставлена. setNeedClientAuth используется для запроса и требования аутентификации сертификата клиента: соединение будет прервано, если не будет представлен подходящий сертификат клиента.

Дополнительную информацию по этой теме можно найти в разделе "Сертификат клиента" спецификации TLS. Немного истории:

  • В версии 1.2 говорится: «Если подходящий сертификат недоступен, клиент ДОЛЖЕН отправить сообщение сертификата, не содержащее сертификатов.». До этого это было просто «СЛЕДУЕТ», но в этом случае клиенты Sun JSSE все равно отправляют пустой список.

  • #P5# <блочная цитата> #P6# #P7# #P8#

Сначала я подумал, что ваш клиент не отправлял свой сертификат, когда вы использовали «необходимость». Действительно, большинство клиентов вообще не будут отправлять клиентский сертификат, если они не могут найти клиентский сертификат, выданный одним из издателей, перечисленных в именах ЦС, отправленных сервером во время его запроса (или если клиент не может построить цепочку самостоятельно, что является распространенной проблемой). По умолчанию JSSE использует центры сертификации в хранилище доверенных сертификатов для создания этого списка. По этой причине ваш клиент, вероятно, вообще не отправит сертификат клиента, если подходящего эмитента нет в хранилище доверенных сертификатов сервера.

Проверить, отправлен ли сертификат клиента, можно с помощью Wireshark. Если вы не используете порт, обычно используемый с SSL/TLS, вам нужно щелкнуть правой кнопкой мыши пакет и выбрать «Декодировать как... -> Транспорт -> SSL».

Там вы должны увидеть сообщение Certificate Request, пришедшее с сервера. (По какой-то причине, когда я использую хранилище доверенных сертификатов JRE по умолчанию с Wireshark, это сообщение отображается как «Зашифрованное сообщение рукопожатия» сразу после сообщения «Обмен ключами сервера». Однако оно не зашифровано: вы можете четко видеть число имен ЦС, если посмотреть на ASCII-рендеринг пакета в нижней панели.Возможно, это потому, что это сообщение слишком длинное, я не уверен.) С более коротким списком, например, хранилище доверия с одним ЦС , Wireshark правильно расшифрует это как сообщение Certificate Request, и вы должны увидеть список принятых ЦС в разделе «Отличительные имена».

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

Предполагая, что у вас есть доступ к тестовому центру сертификации (с клиентским сертификатом, выданным этим центром сертификации), вы можете провести следующие эксперименты.

  • Если вы настроили хранилище доверенных сертификатов с этим тестовым сертификатом ЦС, используйте setWantClientAuth(true), клиент отправит свой сертификат клиента, и соединение будет продолжено. Затем сервер может получить сертификат клиента от SSLSession, как и ожидалось.

  • Если вы используете хранилище доверенных сертификатов по умолчанию (которое не содержит вашего тестового сертификата ЦС), используйте setWantClientAuth(true), DN ЦС не будет в Certificate Request. Клиент отправит сообщение Certificate, но список сертификатов будет пустым (Certificates Length: 0 в Wireshark). Здесь клиент на самом деле не отправляет сертификат клиента, даже если его хранилище ключей настроено на это, просто потому, что он не может найти подходящее совпадение. Соединение продолжится (вы можете получить исключение, если попытаетесь прочитать сертификат узла из SSLSession на сервере, но это не фатально). Это вариант использования setWantClientAuth(true); setNeedClientAuth(true) немедленно прервал бы соединение.

  • Ради этого эксперимента вы можете подделать список DN, отправляемых сервером на Java.

    KeyManagerFactory kmf = //... Initialise a KMF with your server's keystore
    
    TrustManagerFactory tmf = TrustManagerFactory
        .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init((KeyStore) null); // Use the default trust store
    TrustManager[] trustManagers = tmf.getTrustManagers();
    final X509TrustManager origTrustManager = (X509TrustManager) trustManagers[0];
    final X509Certificate caCert = // Load your test CA certificate here.
    X509TrustManager fakeTrustManager = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain,
                String authType) throws CertificateException {
            // Key the behaviour of the default trust manager.
            origTrustManager.checkClientTrusted(chain, authType);
        }
    
        public void checkServerTrusted(X509Certificate[] chain,
                String authType) throws CertificateException {
            // Key the behaviour of the default trust manager.
            origTrustManager.checkServerTrusted(chain, authType);
        }
    
        public X509Certificate[] getAcceptedIssuers() {
            // This is only used for sending the list of acceptable CA DNs.
            return new X509Certificate[] { caCert };
        }
    };
    trustManagers = new X509TrustManager[] { fakeTrustManager };
    
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(kmf.getKeyManagers(), trustManagers, null);
    

    В этом случае сообщение Certificate Request, отправленное сервером, должно содержать DN вашего тестового ЦС. Однако на самом деле диспетчер доверия не доверяет этому ЦС, который по-прежнему использует значения по умолчанию.

    Клиент отправит свой сертификат, но сервер отклонит его, заявив «javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: проверка пути PKIX не удалась», и это завершит соединение. Это, по крайней мере, реализация с использованием провайдера SunJSSE, с использованием менеджеров доверия PKIX или SunX509. Это также соответствует спецификации JSSE. менеджера доверия: "Основная обязанность TrustManager — определить, следует ли доверять представленным учетным данным для аутентификации. Если учетным данным нельзя доверять, соединение будет разорвано."

Ключевым моментом здесь является то, что если вы можете получить клиентский сертификат от SSLSession, этот сертификат должен быть аутентифицирован менеджером доверия (под этим я подразумеваю SSLSession, который вы получаете после завершения рукопожатия, а SSLSocket.getSession() нет). тот, который вы получаете во время рукопожатия с использованием getHandshakeSession(), представленного в Java 7).

Вы, кажется, указываете в комментариях, что используете другого поставщика JSSE, и что ваш клиент все равно отправлял сертификат клиента, независимо от того, находился ли сертификат CA в хранилище доверенных сертификатов сервера, потому что у вас также был другой другой сертификат CA в вашем доверии хранить с тем же DN субъекта. Предполагая, что эти два сертификата ЦС имеют разные ключи (иначе они фактически были бы одним и тем же ЦС), это было бы довольно серьезной ошибкой: приложения, использующие аутентификацию по сертификату клиента, имеют право ожидать, что сертификат клиента был проверен диспетчером доверия ( как указано в справочном руководстве JSSE). Если вы используете Apache Tomcat, после получения сертификата клиента удаленное подключение считается аутентифицированным с помощью этого сертификата. Если в этот момент сервлет сможет использовать клиентский сертификат, который не может быть проверен, то аутентификация на самом деле не была выполнена, что было бы серьезным недостатком.

person Bruno    schedule 14.02.2013
comment
OP говорит, что если сертификат клиента отправлен и не является частью хранилища доверенных сертификатов, согласование также продолжается. Хотя я нахожу это странным. Разве согласование не должно завершиться неудачно? - person Cratylus; 14.02.2013
comment
@Cratylus, OP на самом деле не говорит, что сертификат клиента отправлен. Я только что заставил клиента отправить сертификат, которому не доверяет хранилище доверенных сертификатов по умолчанию, изменив список принятых издателей на сервере, и, как и ожидалось, вы получаете исключение PKIX: сбой соединения. - person Bruno; 14.02.2013
comment
Вы правы. Из ОП неясно (хотя я думаю, что это то, что ОП означает в I noticed this also happens if the client certificate is not part of the truststore). Подождем, пока ОП прояснит это. - person Cratylus; 14.02.2013
comment
@Cratylus Нет, это не должно потерпеть неудачу. Вы думаете о «needClientAuth». - person user207421; 15.02.2013
comment
@Bruno: Когда клиент отправляет ненадежный сертификат, согласование не завершается ошибкой. Сделал вопрос более ясным. Я заметил, что вы упоминаете PKIX исключение. Вы используете PKIX в качестве фабрики доверенных менеджеров? продолжается для неизвестного сертификата. - person Jim; 16.02.2013
comment
@Bruno: Да, я уверен. Я опубликую wireshark в понедельник. Сейчас у меня нет доступа к коду. Кстати, может быть, это связано с тем, что вы используете PKIX, а я использую SunX509? Для trustmanagerfactory? - person Jim; 16.02.2013
comment
@Bruno: Только что понял! По сути, происходит следующее: у меня есть 2 изготовленных на заказ ЦС с одним и тем же CN. Я подписываю 2 разных клиентских сертификата, используя каждый выдающий ЦС. На сервере, которому я доверяю один из двух ЦС. Таким образом, при запросе сертификата запрашивается DN этого ЦС. У клиента действительно есть сертификат, подписанный ЦС с этим CN, НО он фактически подписан другим ЦС. .Клиент отправляет сертификат.Проверка должна завершиться неудачно, но вместо этого устанавливается соединение!Я подумал, что, возможно, это имеет смысл, поскольку, если бы клиент ничего не отправлял, это не имело бы значения - person Jim; 16.02.2013
comment
@Bruno: Почему вы спрашиваете об этом? Да, я могу через servletRequest.getAttribute("javax.servlet.request.X509Certificate");. Я запускаю внутри Tomcat, и этот API возвращает не null, а сертификат клиента. И соединение принимается. Итак, SSLSession действительно возвращает сертификат клиента. Не знаю, почему бы и нет. - person Jim; 17.02.2013
comment
@Bruno: Что вы имеете в виду под it shouldn't really? а) На тот факт, что я могу получить сертификат клиента? Почему его не следует предоставлять или б) что соединение принято? Почему не должно? RFC, на который вы ссылаетесь говорит, что сервер может принять/отклонить соединение. - person Jim; 17.02.2013
comment
@Bruno: Итак, вы говорите, что servletRequest.getAttribute("javax.servlet.request.X509Certificate"); не должен возвращать сертификат клиента. Это наше единственное отличие в нашем тестовом примере, верно? Вы также заметили, что соединение принято, но вы не получаете сертификат клиента? И вы говорите, что это правильно? Почему? Я имею в виду, что клиент уже подключен. Что делает клиента ненадежным только потому, что вы не можете получить сертификат клиента. В любом случае клиент уже имеет доступ к ресурсам, верно? Он подключился - person Jim; 17.02.2013
comment
@Bruno: Просто для понимания. Мы оба замечаем, что если клиент отправляет ненадежный сертификат, JSSE не разрывает соединение. Верно? Единственная разница в том, что я говорю, что могу получить сертификат из SSLSession, а вы не можете .Правильно ли это? Также вы считаете очень плохим тот факт, что при установленном соединении с клиентом, который отправляет ненадежный сертификат, я могу получить сертификат из сеанса. Хотя я не понимаю беспокойства. Почему это важно «Я имею в виду, что он мог бы ничего не отправлять и все еще быть на связи (с want). Так что, если я смогу получить доступ к этому ненадежному - person Jim; 18.02.2013
comment
сертификат?Вы тоже наблюдаете такое поведение или нет?(не хватило места и продолжение здесь комментария) - person Jim; 18.02.2013
comment
@Bruno: С другой точки зрения: RFC, на который вы ссылаетесь, говорит, что сервер может отправить пустой список доверенных DN в запросе сертификата. В этом случае клиент может отправить любой сертификат. Соединение с JSSE будет успешным с want и теперь должен ли я иметь доступ к этому сертификату или нет? Я имею в виду, означает ли пустой список DN, что сервер доверяет каждому ЦС/сертификату? - person Jim; 18.02.2013
comment
@Bruno: Хорошо. В конфигурации у нас есть пользовательский набор SSLImplementation. В рамках реализации код выполняет: ssl.getSession().getPeerCertificateChain(), где ssl - это клиентский сокет. Этот код, который я вижу при отладке, имеет клиентские сертификаты. Позже внутри моего сервлета, если я это сделаю: getAttribute("javax.servlet.request.X509Certificate"); я получу сертификаты. Таким образом, SSLSession действительно содержит одноранговые сертификаты. Вы говорите, что этого не должно быть? Также у него есть clientAuth="want" - person Jim; 18.02.2013
comment
@Bruno: есть настраиваемый менеджер доверенных сертификатов, но он не делает ничего особенного, кроме использования собственного хранилища доверенных сертификатов. Единственное, что я заметил, представляет интерес, это: TrustManagerFactory.getInstance(algorithm, getJSSEProvider()); и поставщик не по умолчанию JSSE, а сторонняя библиотека. Это то, что имеет значение? - person Jim; 18.02.2013
comment
@Джим, наверное. Что это за библиотека? Если он позволяет вам получить сертификат клиента из сеанса SSLSession без проверки того, что сертификат действительно доверенный, он содержит серьезную ошибку. - person Bruno; 18.02.2013
comment
@Бруно:RSA-BSAFE-SHARE. Хотя почему это ошибка? - person Jim; 18.02.2013
comment
@Bruno: Я понимаю, что ты имеешь в виду. В своем тесте ты сказал, что не можешь понять это правильно? - person Jim; 18.02.2013
comment
@ Джим, я отредактировал свой ответ и удалю большинство своих комментариев (я удалю и этот), так как он довольно беспорядочный. В моих тестах соединение завершается ошибкой, если я представляю сертификат клиента, который не может быть проверен в хранилище доверенных сертификатов. - person Bruno; 19.02.2013
comment
@Bruno: это неверно в ответе: The JSSE chooses to send a fatal alert. (setWantAuth could in principle carry on with invalid certificates, but not treat the peer as authenticated, as if no client certificate was sent, but this isn't the case.). Как мы видели в want, соединение не разрывается, следовательно, нет предупреждения о рукопожатии. - person Jim; 19.02.2013
comment
@Jim, это правильно: когда сертификат не отправляется, подключение продолжается, но когда отправляется недействительный сертификат, подключение разрывается. Используемый вами провайдер может этого не делать, но должен, поскольку основная обязанность TrustManager заключается в том, чтобы определить, следует ли доверять представленным учетным данным аутентификации. Если учетные данные не являются доверенными, соединение будет разорвано. - person Bruno; 19.02.2013

Он используется, когда вы хотите узнать учетные данные клиента, если он их отправит, но не хотите прерывать связь, если он этого не сделает. В отличие от «needClientAuth», где рукопожатие не выполняется, если он их не отправляет.

person user207421    schedule 15.02.2013
comment
Мой вопрос: с тех пор, когда клиент отправляет ненадежный сертификат, а соединение продолжается, несмотря на это, когда этот параметр действительно полезен? - person Jim; 16.02.2013
comment
@Jim Клиенту не разрешено отправлять ненадежный сертификат в соответствии с RFC 2246. Когда сервер запрашивает сертификат, он отправляет свои доверенные корневые сертификаты, и клиенту разрешено отправлять сертификат только в том случае, если его цепочка подписантов содержит один из этих доверенные корневые сертификаты. Одним из полезных случаев является то, что если у клиента есть такой сертификат, вам не нужно предоставлять ему форму входа в систему, но если он этого не делает, вы разрешаете продолжение соединения и переходите к форме входа. - person user207421; 16.02.2013
comment
Я вижу, что RFC говорит в 7.4.6:...If no suitable certificate is available, the client should send a certificate message containing no certificates. Это SHOULD не обязательно. Поэтому мне было интересно. Может ли быть так, что, поскольку сертификат является необязательным в wantClientAuth, если отправка сертификата не является доверенной, это не имеет большого значения? Я имею в виду клиента мог бы вообще не отправлять сертификат и все было бы в порядке. Может ли это быть причиной того, что я вижу? Т.е. соединение должно быть принято? - person Jim; 16.02.2013
comment
Также не сказано, что произойдет, если список одобренных эмитентов будет empty. Я имею в виду, что мне просто интересно, что произойдет, если мы установим wantClientAuth и заменим доверительного менеджера поддельным доверительным менеджером. - person Jim; 16.02.2013
comment
Проверьте последний комментарий, который я сделал Бруно. Я понял кое-что о сценарии. - person Jim; 16.02.2013
comment
@Jim Если список принятых эмитентов пуст, клиент вообще не может отправить соответствующий сертификат. - person user207421; 08.05.2013