I've been working on a program to extract information from a dynamic web application, and the program worked fine until I set my tomcat server to use SSL using a self-signed(thus, untrusted) certificate. The stack trace of the error is:
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.<init>(Controller.java:49) at com.certicom.gls.glscs.nongui.Controller.main(Controller.java:61)
In a web browser, the user is prompted a warning when accessing a HTTPS site with an untrusted certificate, and asked to make an exception if he likes to proceed; I would like to implement a similar functionality for my command-line application... I admit that I am new to socket programming and networking in general; any advice solving this problem will be great!
The steps to install a new certificate into the Java default truststore are: extract cert from server: openssl s_client -connect server:443. import certificate into truststore using keytool: keytool -import -alias alias.server.com -keystore $JAVA_HOME/jre/lib/security/cacerts.
Here is some relevant code:
// 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) { }
This will completely disable SSL checking—just don't learn exception handling from such code!
To do what you want, you would have to implement a check in your TrustManager that prompts the user.
Following code from here is a useful solution. No keystores etc. Just call method SSLUtilities.trustAllHttpsCertificates() before initializing the service and port (in 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With