3

I'm currently trying to access a URL with both HTTP and HTTPS. The URL that I'm trying to access requires basic authentication. With HTTP it works fine, but with HTTPS it doesn't. I'm not sure if there's anything I need to add differently with HTTPS. The URL is supposed to return to me text that is in key value format that I can load into a Properties Object.

Here is the code I have tried so far.

if (cpUrl.getProtocol().equals("https")) {
                        out.println("https", 0);
                        HttpsURLConnection connection = (HttpsURLConnection) cpUrl.openConnection();

                        TrustManager[] trustAllCerts = new TrustManager[] { new  BusinessIntelligenceX509TrustManager() };
                        SSLContext sc;

                        try {
                            sc = SSLContext.getInstance("SSL");
                        }
                        catch (NoSuchAlgorithmException noSuchAlgorithmException) {
                            return;
                        }

                        HostnameVerifier hv = new BusinessIntelligenceHostnameVerifier();

                        try {
                            sc.init(null, trustAllCerts, new java.security.SecureRandom());
                        }
                        catch (KeyManagementException keyManagementException) {

                            return;
                        }

                        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
                        HttpsURLConnection.setDefaultHostnameVerifier(hv);

                        connection.setDoInput(true);
                        connection.setRequestProperty("Authorization", "Basic " + encode);

                        connection.setRequestMethod("POST");
                        connection.connect();
                        stream = connection.getInputStream();
                        Properties properties = new Properties();
                        properties.load(stream);

                    }

Here are the certificate classes

   //HTTPS CERTIFICATE CLASSES
    class BusinessIntelligenceHostnameVerifier implements HostnameVerifier {

        public boolean verify(String arg0, SSLSession arg1) {
            return true;
        }

    }

    class BusinessIntelligenceX509TrustManager implements X509TrustManager {

        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            // no-op
        }

        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            // no-op
        }

    }

The error message when I remove all the certificate code (as well as with the certificate code):

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 com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1518)
    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:818)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1030)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1057)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1041)
    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.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)
    at com.tecsys.bi.install.BiInstall2ControlPanelPromptsProcessor.run(BiInstall2ControlPanelPromptsProcessor.java:117)
    at java.lang.Thread.run(Thread.java:595)
Caused by: 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.validator.PKIXValidator.doBuild(PKIXValidator.java:221)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:145)
    at sun.security.validator.Validator.validate(Validator.java:203)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
    at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:841)
    ... 12 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:236)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:194)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:216)
    ... 17 more
RMT
  • 7,040
  • 4
  • 25
  • 37
  • Where does your code fail? Does it fail in connection setup, in basic authentication or elsewhere? – Vineet Reynolds Jun 09 '11 at 12:33
  • It fails when loading properties. It says theres nothing there. Even though there is. – RMT Jun 09 '11 at 12:35
  • On a different note, implementing the hostname verifier and trust manager classes in the manner demonstrated, is redundant and insecure. If your `cacerts` file has been loaded with the appropriate certificates, you shouldn't have a problem with the SSL/TLS setup phase. – Vineet Reynolds Jun 09 '11 at 12:35
  • >'Let me know if need anythign else.' -- The error message, off course – Tahir Akhtar Jun 09 '11 at 12:36
  • I suppose you meant this line - `properties.load(stream);`. – Vineet Reynolds Jun 09 '11 at 12:36
  • I'm a little puzzled. Does the server have a self-signed cert? Or a cert whose root CA is not known to Java? Do you need to use a client cert? If none of the above, then HTTPS should just work ... without any custom coding. The issue of Basic Authentication should be orthogonal, because that stuff all happens after the TLS connection has been established. – Stephen C Jun 09 '11 at 12:38
  • None of the above I think, before i originally had no certifcate code and it was the same as http but i kept getting exception saying I needed a certificate. – RMT Jun 09 '11 at 12:43
  • @RMT, what happens when you visit the URL in the browser? Does the browser display the contents of the Property? Also, have you also tried dumping the contents of the connection's input stream into System.out or a logger? – Vineet Reynolds Jun 09 '11 at 12:46
  • @Vineer Reynolds, when i visit url in the browser, it asks me for my username and password, and it works just fine. – RMT Jun 09 '11 at 12:48
  • @RMT, Sorry, I should have been more specific. This is the HTTPS link I was referring to. Also, did you notice any HTTP redirects being issued, after login via the HTTPS channel? I'm suspecting that the underlying behavior of the server is different for HTTP and HTTPS, resulting in a different response for HTTPS. – Vineet Reynolds Jun 09 '11 at 12:50
  • There is no redirection. – RMT Jun 09 '11 at 12:51
  • @RMT, grasping at straws now. Is the encoding of the response ISO-8859-1 in the case of HTTPS? `Properties` objects can be loaded from streams and files only if the encoding is the one afore mentioned one. – Vineet Reynolds Jun 09 '11 at 12:57
  • @Vineer Reynolds, I made a mistake, it fails when it tries to connect. im sorry i thought it was something else. – RMT Jun 09 '11 at 13:05

1 Answers1

3

The problem appears to be with the fact that the connection is opened, before the SSLContext and HostNameVerifier instances are changed for the connection. This is not possible, for the SSL/TLS handshake occurs even before the contents of the connection are read from an InputStream.

In other words, the following line

HttpsURLConnection connection = (HttpsURLConnection) cpUrl.openConnection();

ought to execute only after the SSLContext and HostNameVerifiers instances have been registered with the HttpsURLConnection class.

HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(hv);

Fixing this sequence, ought to fix the problem, for the handshake will now occur using the new parameters.

Vineet Reynolds
  • 76,006
  • 17
  • 150
  • 174
  • 2
    @RMT, you're welcome. After your last update, I noticed that your custom trust manager and hostname verifiers were not used to resolve the cert trust path. That bit of info, was all that I needed. – Vineet Reynolds Jun 09 '11 at 13:33