почему Java не отправляет сертификат клиента во время подтверждения SSL?
Я пытаюсь подключиться к безопасному веб-сервису.
Я получал отказ от рукопожатия, хотя мое хранилище ключей и доверительное хранилище были установлены правильно.
После нескольких дней разочарования, бесконечного поиска и выяснения всех вокруг я узнал, что единственная проблема заключалась в том, что Java решил не отправлять клиентский сертификат на сервер во время рукопожатия.
- SSLHandshakeException: нет альтернативных имен объектов
- Java SSL: как отключить проверку имени хоста
- Подтверждения IPN PayPal IPN с использованием процедур SSL: SSL3_READ_BYTES: сбой вызова подтверждения sslv3
- Тип Keystore: какой из них использовать?
- Как сделать HTTPS POST с Android?
В частности:
- Сервер запросил сертификат клиента (CN = RootCA) – то есть «дайте мне сертификат, подписанный корневым ЦС»,
- Java заглянула в хранилище ключей и только нашла мой клиентский сертификат, подписанный «SubCA», который, в свою очередь, выдается «RootCA». Он не потрудился заглянуть в траст-магазин … duh OK Я думаю
- К сожалению, когда я попытался добавить сертификат «SubCA» в хранилище ключей, это совсем не помогло. Я проверил, загружены ли сертификаты в хранилище ключей. Они делают, но KeyManager игнорирует все сертификаты, кроме клиентского.
- Все вышеизложенное приводит к тому, что java решает, что у него нет сертификатов, удовлетворяющих запросу сервера, и ничего не посылает … tadaaa handshake failure 🙁
Мои вопросы:
- Возможно ли, что я добавил сертификат «SubCA» в хранилище ключей так, чтобы «сломал цепочку сертификатов» или что-то такое, что KeyManager загружает только сертификат клиента и игнорирует остальные? (Chrome и openssl справятся с этим так, почему они не могут быть java?), Обратите внимание, что сертификат SubCA всегда представляется отдельно как доверенный орган, поэтому Chrome, по-видимому, правильно упаковывает его вместе с сертификатом клиента во время рукопожатия)
- Является ли это официальной «проблемой конфигурации» на стороне сервера? Сервер является третьим лицом. Я ожидаю, что сервер запросит сертификат, подписанный органом «SubCA», поскольку это то, что они нам предоставили. Я подозреваю, что тот факт, что это работает в Chrome и openssl, заключается в том, что они «менее ограничительные», а java просто «по книге» и не удается.
Мне удалось собрать грязный обходной путь для этого, но я не очень доволен этим, поэтому буду рад, если кто-нибудь сможет прояснить это для меня.
- Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL-квитирование отменено: ошибка в библиотеке SSL, обычно ошибка протокола
- SSL / TLS vs SSL / TLS-VPN
- Можно ли заставить Java игнорировать «хранилище доверия» и просто принять любой сертификат SSL, который он получает?
- Инициализация SSLContext
- Использование самозаверяющего сертификата с .NET HttpWebRequest / Response
- Игнорирование сертификата SSL в Apache HttpClient 4.3
- Не удалось найти допустимый путь сертификации для запрошенной целевой ошибки даже после импорта сертификата
- Как заставить прокси-сервер charles работать с nougat 7 Android?
Возможно, вы могли импортировать промежуточный сертификат ЦС в хранилище ключей, не связывая его с записью, в которой у вас есть сертификат клиента и его закрытый ключ. Вы должны увидеть это с помощью keytool -v -list -keystore store.jks
. Если вы получаете только один сертификат на запись псевдонима, они не вместе.
Вам нужно будет импортировать свой сертификат и его цепочку вместе в псевдоним хранилища ключей, у которого есть ваш закрытый ключ.
Чтобы узнать, в каком ключевом псевдониме есть закрытый ключ, используйте keytool -list -keystore store.jks
(здесь я предполагаю тип магазина keytool -list -keystore store.jks
). Это скажет вам следующее:
Your keystore contains 1 entry myalias, Feb 15, 2012, PrivateKeyEntry, Certificate fingerprint (MD5): xxxxxxxx
Здесь псевдоним – myalias
. Если вы используете -v
в дополнение к этому, вы должны увидеть Alias Name: myalias
.
Если у вас его нет отдельно, экспортируйте сертификат своего клиента из хранилища ключей:
keytool -exportcert -rfc -file clientcert.pem -keystore store.jks -alias myalias
Это должно дать вам файл PEM.
Используя текстовый редактор (или cat
), подготовьте файл (давайте назовем его bundle.pem
) с этим сертификатом клиента и промежуточным сертификатом ЦС (и, возможно, самим сертификатом корневого ЦС, если вы хотите), чтобы клиентский сертификат находился на начало, и его сертификат эмитента находится под.
Это должно выглядеть так:
-----BEGIN CERTIFICATE----- MIICajCCAdOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJVSzEa .... -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIICkjCCAfugAwIBAgIJAKm5bDEMxZd7MA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNV .... -----END CERTIFICATE-----
Теперь импортируйте этот пакет обратно в псевдоним, где находится ваш закрытый ключ:
keytool -importcert -keystore store.jks -alias myalias -file bundle.pem
В качестве добавления здесь вы можете использовать %> openssl s_client -connect host.example.com:443 и посмотреть дамп и проверить, что весь основной сертификат действителен для клиента. Вы ищете это в нижней части вывода. Проверьте код возврата: 0 (ok)
Если вы добавите -showcerts, он сбрасывает всю информацию связки ключей, которая была отправлена вместе с сертификатом хоста, и это то, что вы загрузили в свою цепочку ключей.