Как получить сертификат ненадежного SSL-сервера для проверки и доверия к нему?
Моя проблема:
Я хочу подключиться к серверам (не ограничивающимся протоколом HTTPS - это может быть LDAP-over-SSL, может быть SMTPS, может быть IMAPS и т. Д.), Которые могут использовать сертификаты, которые Java не будет доверять по умолчанию (потому что они самозаверяющими).
Требуемый рабочий процесс состоит в том, чтобы попытаться установить соединение, получить информацию о сертификате, представить ее пользователю и, если он ее принимает, добавить ее в склад доверенных сертификатов, чтобы в будущем ей можно было доверять.
Я застрял при получении сертификата. У меня есть код (см. В конце статьи), который я получил здесь и с сайтов, на которые указывают ответы на вопросы о java SSL. Код просто создаетSSLSocket
запускает рукопожатие SSL и запрашивает сеанс SSL дляCertificate[]
, Код работает нормально, когда я подключаюсь к серверу, используя уже надежный сертификат. Но когда я подключаюсь к серверу с помощью самоподписанного сертификата, я получаю обычное:
<code>Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.ssl.Alerts.getSSLException(Unknown Source) at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source) at sun.security.ssl.Handshaker.fatalSE(Unknown Source) at sun.security.ssl.Handshaker.fatalSE(Unknown Source) at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source) [etc] </code>
Если я бегу с-Djavax.net.debug=all
Я вижу, что JVM извлекает самозаверяющий сертификат, но разрывает соединение для использования ненадежного сертификата, прежде чем доберется до точки, где он вернет сертификаты.
Похоже, проблема курицы и яйца. Это не позволит мне увидеть сертификаты, потому что им не доверяют. Но мне нужно увидеть сертификаты, чтобы можно было добавить их в склад доверенных сертификатов, чтобы им доверяли. Как вы выходите из этого?
Например, если я запускаю программу как:
<code>java SSLTest www.google.com 443 </code>
Я получил распечатку сертификатов, которые использует Google. Но если я запускаю это как
<code>java SSLTest my.imap.server 993 </code>
Я получаю исключение, указанное выше.
Код:
<code>import java.io.InputStream; import java.io.OutputStream; import java.security.cert.*; import javax.net.SocketFactory; import javax.net.ssl.*; public class SSLTest { public static void main(String[] args) throws Exception { if (args.length != 2) { System.err.println("Usage: SSLTest host port"); return; } String host = args[0]; int port = Integer.parseInt(args[1]); SocketFactory factory = SSLSocketFactory.getDefault(); SSLSocket socket = (SSLSocket) factory.createSocket(host, port); socket.startHandshake(); Certificate[] certs = socket.getSession().getPeerCertificates(); System.out.println("Certs retrieved: " + certs.length); for (Certificate cert : certs) { System.out.println("Certificate is: " + cert); if(cert instanceof X509Certificate) { try { ( (X509Certificate) cert).checkValidity(); System.out.println("Certificate is active for current date"); } catch(CertificateExpiredException cee) { System.out.println("Certificate is expired"); } } } } } </code>