why doesn't java send the client certificate during SSL handshake?
почему java не отправляет клиентский сертификат во время SSL-квитирования?
Я пытаюсь подключиться к безопасному веб-сервису.
Я получал сбой квитирования, хотя мое хранилище ключей и truststore были установлены правильно.
После нескольких дней разочарования, бесконечного поиска в Google и расспросов всех вокруг я выяснил, что единственной проблемой было то, что java решила не отправлять клиентский сертификат на сервер во время квитирования.
В частности:
Сервер запросил клиентский сертификат (CN = RootCA) - т.е. "дайте мне сертификат, подписанный корневым центром сертификации"
Java заглянула в хранилище ключей и нашла только мой клиентский сертификат, подписанный "SubCA", который, в свою очередь, выдается "RootCA". Она не потрудилась заглянуть в truststore...думаю, все в порядке
К сожалению, когда я попытался добавить сертификат "SubCA" в хранилище ключей, это вообще не помогло. Я проверил, загружаются ли сертификаты в хранилище ключей. Они отправляют, но KeyManager игнорирует все сертификаты, кроме клиентского.
Все вышесказанное приводит к тому, что java решает, что у нее нет сертификатов, удовлетворяющих запросу сервера, и ничего не отправляет...tadaaa сбой квитирования :-(
Мои вопросы:
Возможно ли, что я добавил сертификат "SubCA" в хранилище ключей способом, который "разорвал цепочку сертификатов" или что-то в этом роде, так что KeyManager загружает только клиентский сертификат и игнорирует остальные? (Chrome и openssl справляются с этим, так почему java не может? - обратите внимание, что сертификат "SubCA" всегда представлен отдельно как доверенный орган, поэтому Chrome, по-видимому, корректно упаковывает его вместе с клиентским сертификатом во время квитирования)
Это формальная "проблема с конфигурацией" на стороне сервера? Сервер является сторонним. Я бы ожидал, что сервер запросит сертификат, подписанный полномочным органом "SubCA", поскольку именно это они нам предоставили. Я подозреваю, что тот факт, что это работает в Chrome и openssl, объясняется тем, что они "менее ограничительны", а java просто работает "по инструкции" и дает сбой.
Мне удалось найти грязный обходной путь для этого, но я не очень доволен этим, поэтому я буду рад, если кто-нибудь сможет прояснить это для меня.
Переведено автоматически
Ответ 1
Возможно, вы импортировали промежуточный сертификат CA в хранилище ключей, не связывая его с записью, в которой у вас есть клиентский сертификат и его закрытый ключ. Вы должны быть в состоянии увидеть это с помощью keytool -v -list -keystore store.jks. Если вы получаете только один сертификат для каждой записи псевдонима, они не объединены.
Вам нужно будет импортировать ваш сертификат и его цепочку вместе в псевдоним хранилища ключей, который содержит ваш закрытый ключ.
Чтобы узнать, какой псевдоним хранилища ключей содержит закрытый ключ, используйте keytool -list -keystore store.jks (Я предполагаю, что здесь используется тип хранилища JKS). Это подскажет вам что-то вроде этого:
Your keystore contains 1 entry
myalias, Feb 15, 2012, PrivateKeyEntry, Certificate fingerprint(MD5): xxxxxxxx
Здесь используется псевдоним myalias. Если вы используете -v в дополнение к этому, вы должны увидеть Alias Name: myalias.
Если у вас его еще нет отдельно, экспортируйте клиентский сертификат из хранилища ключей:
Используя текстовый редактор (или cat), подготовьте файл (назовем его bundle.pem) с этим клиентским сертификатом и промежуточным сертификатом CA (и, возможно, с самим сертификатом корневого CA, если хотите), чтобы клиентский сертификат был в начале, а сертификат его эмитента - чуть ниже.
As an add here, you can use %> openssl s_client -connect host.example.com:443 and see the dump and check that all the main cert is valid against the client. You are looking for this at the bottom of the output. Verify return code: 0 (ok)
If you add -showcerts it will dump all the info of the keychain that was sent along with the host certificate, which is what you loaded into your keychain.
Ответ 3
Most of the solutions I've seen revolve around using the keytool and none of them matched my case.
Here is a very brief description: I've got a PKCS12 (.p12) which works fine in Postman with disabled certificate verification, however programmatically I always ended up getting server error "400 Bad Request" / "No required SSL certificate was sent".
The reason was a missing TLS extension SNI (Server Name Indication) and following is the solution.
The thing is when using client cert signed by an intermediate one you need to include the intermediate one in your trustore so java can find it. Either solo or as a bundle with the root issuing ca.