Jak odzyskać niezaufany certyfikat serwera SSL, aby go przejrzeć i zaufać?
Mój problem:
Chcę połączyć się z serwerami (nie ograniczając się do protokołu HTTPS - może to być LDAP-over-SSL, może to być SMTPS, może być IMAPS itp.), Które mogą używać certyfikatów, którym Java nie będzie ufać domyślnie (ponieważ są z podpisem własnym).
Żądany przepływ pracy to próba nawiązania połączenia, pobranie informacji o certyfikacie, przedstawienie go użytkownikowi, a jeśli go zaakceptuje, dodanie go do magazynu zaufanych certyfikatów, aby można było zaufać.
Utknąłem w odzyskaniu certyfikatu. Mam kod (zobacz na końcu posta), który pobrałem z tego miejsca i ze stron wskazanych przez odpowiedzi na pytania dotyczące java SSL. Kod po prostu tworzySSLSocket
, uruchamia uzgadnianie SSL i prosi o sesję SSL dlaCertificate[]
. Kod działa poprawnie, gdy łączę się z serwerem za pomocą certyfikatu, który jest już zaufany. Ale kiedy łączę się z serwerem za pomocą samopodpisanego certyfikatu, otrzymuję zwykłe:
<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>
Jeśli ucieknę-Djavax.net.debug=all
Widzę, że maszyna JVM pobiera certyfikat samopodpisany, ale przedłuży połączenie, aby użyć niezaufanego certyfikatu, zanim dotrze do punktu, w którym zwróci certyfikaty.
Wygląda na problem z kurczakiem i jajkiem. Nie pozwoli mi zobaczyć certyfikatów, ponieważ nie są one zaufane. Ale muszę zobaczyć certyfikaty, aby móc dodać je do magazynu zaufanych certyfikatów. Jak się z tego wyrwać?
Na przykład, jeśli uruchomię program jako:
<code>java SSLTest www.google.com 443 </code>
Dostaję wydruk certyfikatów, których używa Google. Ale jeśli go uruchomię
<code>java SSLTest my.imap.server 993 </code>
Dostaję wyjątek, o którym mowa powyżej.
Kod:
<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>