Permitindo que o Java use um certificado não confiável para conexão SSL / HTTPS

Eu tenho trabalhado em um programa para extrair informações de um aplicativo da web dynamic, e o programa funcionou bem até que eu configurei meu servidor tomcat para usar o SSL usando um certificado auto-assinado (assim, não confiável). O rastreamento de pilha do erro é:

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 Error: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1584) at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:174) at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:168) at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:848) at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:106) at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:495) at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:433) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:877) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1089) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1116) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1100) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:402) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:170) at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:857) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230) at com.certicom.gls.glscs.nongui.URLReader$PostURL.setupURL(URLReader.java:34) at com.certicom.gls.glscs.nongui.URLReader.getLogin(URLReader.java:227) at com.certicom.gls.glscs.nongui.URLReader.testLogin(URLReader.java:436) at com.certicom.gls.glscs.nongui.Controller.loginMenu(Controller.java:384) at com.certicom.gls.glscs.nongui.Controller.menu(Controller.java:324) at com.certicom.gls.glscs.nongui.Controller.(Controller.java:49) at com.certicom.gls.glscs.nongui.Controller.main(Controller.java:61) 

Em um navegador da Web, o usuário recebe um aviso ao acessar um site HTTPS com um certificado não confiável e solicita uma exceção caso ele goste de prosseguir; Eu gostaria de implementar uma funcionalidade semelhante para o meu aplicativo de linha de comando … Eu admito que sou novo em programação de soquete e rede em geral; qualquer conselho resolvendo este problema será ótimo!

Aqui está algum código relevante:

 // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) { } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) { } } }; // Install the all-trusting trust manager try { SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (Exception e) { } // Now you can access an https URL without having the certificate in the truststore try { URL url = new URL("https://hostname/index.html"); } catch (MalformedURLException e) { } 

Isso desabilitará completamente a verificação de SSL – apenas não aprenda o tratamento de exceções desse código!

Para fazer o que você quer, você teria que implementar uma verificação no seu TrustManager que avisa o usuário.

Seguir o código daqui é uma solução útil. Nenhum keystores, etc. Basta chamar o método SSLUtilities.trustAllHttpsCertificates () antes de inicializar o serviço e a porta (no SOAP).

 import java.security.GeneralSecurityException; import java.security.SecureRandom; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; /** * This class provide various static methods that relax X509 certificate and * hostname verification while using the SSL over the HTTP protocol. * * @author Jiramot.info */ public final class SSLUtilities { /** * Hostname verifier for the Sun's deprecated API. * * @deprecated see {@link #_hostnameVerifier}. */ private static com.sun.net.ssl.HostnameVerifier __hostnameVerifier; /** * Thrust managers for the Sun's deprecated API. * * @deprecated see {@link #_trustManagers}. */ private static com.sun.net.ssl.TrustManager[] __trustManagers; /** * Hostname verifier. */ private static HostnameVerifier _hostnameVerifier; /** * Thrust managers. */ private static TrustManager[] _trustManagers; /** * Set the default Hostname Verifier to an instance of a fake class that * trust all hostnames. This method uses the old deprecated API from the * com.sun.ssl package. * * @deprecated see {@link #_trustAllHostnames()}. */ private static void __trustAllHostnames() { // Create a trust manager that does not validate certificate chains if (__hostnameVerifier == null) { __hostnameVerifier = new SSLUtilities._FakeHostnameVerifier(); } // if // Install the all-trusting host name verifier com.sun.net.ssl.HttpsURLConnection .setDefaultHostnameVerifier(__hostnameVerifier); } // __trustAllHttpsCertificates /** * Set the default X509 Trust Manager to an instance of a fake class that * trust all certificates, even the self-signed ones. This method uses the * old deprecated API from the com.sun.ssl package. * * @deprecated see {@link #_trustAllHttpsCertificates()}. */ private static void __trustAllHttpsCertificates() { com.sun.net.ssl.SSLContext context; // Create a trust manager that does not validate certificate chains if (__trustManagers == null) { __trustManagers = new com.sun.net.ssl.TrustManager[]{new SSLUtilities._FakeX509TrustManager()}; } // if // Install the all-trusting trust manager try { context = com.sun.net.ssl.SSLContext.getInstance("SSL"); context.init(null, __trustManagers, new SecureRandom()); } catch (GeneralSecurityException gse) { throw new IllegalStateException(gse.getMessage()); } // catch com.sun.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(context .getSocketFactory()); } // __trustAllHttpsCertificates /** * Return true if the protocol handler property java. protocol.handler.pkgs * is set to the Sun's com.sun.net.ssl. internal.www.protocol deprecated * one, false otherwise. * * @return true if the protocol handler property is set to the Sun's * deprecated one, false otherwise. */ private static boolean isDeprecatedSSLProtocol() { return ("com.sun.net.ssl.internal.www.protocol".equals(System .getProperty("java.protocol.handler.pkgs"))); } // isDeprecatedSSLProtocol /** * Set the default Hostname Verifier to an instance of a fake class that * trust all hostnames. */ private static void _trustAllHostnames() { // Create a trust manager that does not validate certificate chains if (_hostnameVerifier == null) { _hostnameVerifier = new SSLUtilities.FakeHostnameVerifier(); } // if // Install the all-trusting host name verifier: HttpsURLConnection.setDefaultHostnameVerifier(_hostnameVerifier); } // _trustAllHttpsCertificates /** * Set the default X509 Trust Manager to an instance of a fake class that * trust all certificates, even the self-signed ones. */ private static void _trustAllHttpsCertificates() { SSLContext context; // Create a trust manager that does not validate certificate chains if (_trustManagers == null) { _trustManagers = new TrustManager[]{new SSLUtilities.FakeX509TrustManager()}; } // if // Install the all-trusting trust manager: try { context = SSLContext.getInstance("SSL"); context.init(null, _trustManagers, new SecureRandom()); } catch (GeneralSecurityException gse) { throw new IllegalStateException(gse.getMessage()); } // catch HttpsURLConnection.setDefaultSSLSocketFactory(context .getSocketFactory()); } // _trustAllHttpsCertificates /** * Set the default Hostname Verifier to an instance of a fake class that * trust all hostnames. */ public static void trustAllHostnames() { // Is the deprecated protocol setted? if (isDeprecatedSSLProtocol()) { __trustAllHostnames(); } else { _trustAllHostnames(); } // else } // trustAllHostnames /** * Set the default X509 Trust Manager to an instance of a fake class that * trust all certificates, even the self-signed ones. */ public static void trustAllHttpsCertificates() { // Is the deprecated protocol setted? if (isDeprecatedSSLProtocol()) { __trustAllHttpsCertificates(); } else { _trustAllHttpsCertificates(); } // else } // trustAllHttpsCertificates /** * This class implements a fake hostname verificator, trusting any host * name. This class uses the old deprecated API from the com.sun. ssl * package. * * @author Jiramot.info * * @deprecated see {@link SSLUtilities.FakeHostnameVerifier}. */ public static class _FakeHostnameVerifier implements com.sun.net.ssl.HostnameVerifier { /** * Always return true, indicating that the host name is an acceptable * match with the server's authentication scheme. * * @param hostname the host name. * @param session the SSL session used on the connection to host. * @return the true boolean value indicating the host name is trusted. */ public boolean verify(String hostname, String session) { return (true); } // verify } // _FakeHostnameVerifier /** * This class allow any X509 certificates to be used to authenticate the * remote side of a secure socket, including self-signed certificates. This * class uses the old deprecated API from the com.sun.ssl package. * * @author Jiramot.info * * @deprecated see {@link SSLUtilities.FakeX509TrustManager}. */ public static class _FakeX509TrustManager implements com.sun.net.ssl.X509TrustManager { /** * Empty array of certificate authority certificates. */ private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{}; /** * Always return true, trusting for client SSL chain peer certificate * chain. * * @param chain the peer certificate chain. * @return the true boolean value indicating the chain is trusted. */ public boolean isClientTrusted(X509Certificate[] chain) { return (true); } // checkClientTrusted /** * Always return true, trusting for server SSL chain peer certificate * chain. * * @param chain the peer certificate chain. * @return the true boolean value indicating the chain is trusted. */ public boolean isServerTrusted(X509Certificate[] chain) { return (true); } // checkServerTrusted /** * Return an empty array of certificate authority certificates which are * trusted for authenticating peers. * * @return a empty array of issuer certificates. */ public X509Certificate[] getAcceptedIssuers() { return (_AcceptedIssuers); } // getAcceptedIssuers } // _FakeX509TrustManager /** * This class implements a fake hostname verificator, trusting any host * name. * * @author Jiramot.info */ public static class FakeHostnameVerifier implements HostnameVerifier { /** * Always return true, indicating that the host name is an acceptable * match with the server's authentication scheme. * * @param hostname the host name. * @param session the SSL session used on the connection to host. * @return the true boolean value indicating the host name is trusted. */ public boolean verify(String hostname, javax.net.ssl.SSLSession session) { return (true); } // verify } // FakeHostnameVerifier /** * This class allow any X509 certificates to be used to authenticate the * remote side of a secure socket, including self-signed certificates. * * @author Jiramot.info */ public static class FakeX509TrustManager implements X509TrustManager { /** * Empty array of certificate authority certificates. */ private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{}; /** * Always trust for client SSL chain peer certificate chain with any * authType authentication types. * * @param chain the peer certificate chain. * @param authType the authentication type based on the client * certificate. */ public void checkClientTrusted(X509Certificate[] chain, String authType) { } // checkClientTrusted /** * Always trust for server SSL chain peer certificate chain with any * authType exchange algorithm types. * * @param chain the peer certificate chain. * @param authType the key exchange algorithm used. */ public void checkServerTrusted(X509Certificate[] chain, String authType) { } // checkServerTrusted /** * Return an empty array of certificate authority certificates which are * trusted for authenticating peers. * * @return a empty array of issuer certificates. */ public X509Certificate[] getAcceptedIssuers() { return (_AcceptedIssuers); } // getAcceptedIssuers } // FakeX509TrustManager } // SSLUtilities 

Outra opção é obter um arquivo “.pem” (chave pública) para esse servidor em particular, e instalá-lo localmente no coração do arquivo “cacerts” do JRE, então ele poderá fazer o download desse servidor sem reclamar, sem comprometer toda a estrutura SSL da sua JVM em execução e permitindo o download de outros servidores certificados desconhecidos …