user207421 почти правильно. Чтобы быть более точным:
Когда вы, как клиент, открываете соединение SSL / TLS с сервером, в рамках рукопожатия сервер отправляет свой сертификат «цепочка», содержащий собственный сертификат, плюс обычно один или несколько связанных ЦС. (Certificate Authority) сертификаты, заканчивающиеся «корневым» CA, которому следует доверять. См. Соседний стек https://security.stackexchange.com/questions/20803/how-does-ssl-work/ для получения эпически полного объяснения. На общедоступных серверах обычно используется сертификат, выданный и подписанный общедоступным центром сертификации, таким как Digicert, GoDaddy, LetsEncrypt / ISRG, которые уже находятся в стандартном хранилище доверенных сертификатов по умолчанию (для Java в файле JRE/lib/security/cacerts
), поэтому никаких действий не требуется. Если сервер использует сертификат от стороннего или частного ЦС или самозаверяющий сертификат (вообще не от ЦС), то (для Java) некоторый сертификат в цепочке должен быть добавлен кклиентское доверенное хранилище или иным образом переопределено;это должен быть сертификат server только в том случае, если сертификат сервера является самоподписанным (который сам по себе является цепочкой и не имеет соответствующих сертификатов CA).
Java / JSSE реализует это через SSLContext
, который содержит, помимо прочего, TrustManager
, более конкретно, X509ExtendedTrustManager
, который инициализируется из хранилища доверенных сертификатов. Вы можете создать SSLContext
явно в коде из любого набора доверенных сертификатов (который даже не должен быть из файла), или использовать контекст по умолчанию, который использует файл склада доверенных сертификатов по умолчанию, который по умолчанию имеет имя файла выше, если не переопределеносистемным свойством.
Когда цепочка сертификатов сервера получена, она передается в TrustManager контекста для проверки;среди (многих!) других проверок, на каждом уровне нормальной цепочки или на отдельном уровне самоподписанного сертификата, JSSE TrustManager ищет сертификат привязки с тем же Subject и (Subject) PublicKey и, если это так, использует его дляпроверить цепочку сертификатов. Обратите внимание, что обычный (выданный CA) листовой сертификат может иметь пустую тему, если вместо него используется альтернативное имя субъекта - см. Rfc5280 и rfc2818 - но самозаверяющий сертификат не может этого сделать, поскольку у него есть Subject = Issuer и эмитент должен не быть пустым. Предполагается, что сертификаты для разных объектов (например, для разных серверов) обычно имеют разные ключи, хотя один объект может иметь несколько сертификатов с одним и тем же ключом или разными ключами и может соответствовать нескольким именам и / или адресам серверов.
Если сертификат считается действительным в целом, для некоторых приложений TLS, в частности HTTPS, валидатор также проверяет, что это правильный сервер, в частности, что атрибут CommonName в темеполе или запись в дополнительном дополнительном имени субъекта, если таковая имеется - которая для общедоступных ЦС существует не менее десяти лет, соответствует DNS-имени хоста или IP-адресу в URL-адресе. В более старых версиях Java (до 6 IIRC) это делалось не в JSSE, а в вызывающем приложении (ах) или библиотеке, такой как HttpsURLConnection
, которая, как унаследованная версия, по-прежнему имеет возможность использовать вместо нее собственную HostnameVerifier
.
Все это можно изменить с помощью пользовательского TrustManager
вместо стандартного, и некоторые вещи, такие как Apache HttpClient
, делают это правильно, но вы найдете здесь (слишком много) ответов инекоторые другие Стеки, рекомендующие вам «устранять» ошибки TLS с использованием стерилизованного TrustManager
, который просто принимает любой сертификат, независимо от того, является ли он действительным и правильным, и, таким образом, успешно подключается и отправляет конфиденциальные данные или принимает изменения от любого злоумышленникакому удается перехватить IP-трафик, что в наше время часто довольно просто.