Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accepting certificates in Java

I'm having problems interacting with an HTTPS site via Java. My program uses one URL with an untrusted certificate each time the program runs. This program has to run on more than one system. Currently, I have the following:

public class A{
    HostnameVerifier hv = new HostnameVerifier(){
        public boolean verify(String urlHostName, SSLSession session){
            return true;
        }       
    };

    HttpsURLConnection.setDefaultHostnameVerifier(hv);

    javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
    javax.net.ssl.TrustManager tm = new miTM();
    trustAllCerts[0] = tm;
    javax.net.ssl.SSLContext sc = null;
    try {
        sc = javax.net.ssl.SSLContext.getInstance("SSL");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    try {
        sc.init(null, trustAllCerts, null);
    } catch (KeyManagementException e) {
        e.printStackTrace();
    }
   javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
class miTM implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager{

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

    public boolean isServerTrusted(java.security.cert.X509Certificate[] certs){
        return true;
        }

    public boolean isClientTrusted(java.security.cert.X509Certificate[] certs){
        return true;
    }

    public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException{
        return;
    }

    public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException{
        return;
        }
}

With this code, I can perform the following just fine:

URL url = new URL(urlString);
URLConnection cnx = url.openConnection();
cnx.connect();

InputStream ins = cnx.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(ins));
String curline;
while( (curline = in.readLine()) != null ) {
     System.out.println(curline);
}

However, I cannot do the following:

httpClient = new HttpClient();
PostMethod postMethod = null;
int intResult = 0;
postMethod = new PostMethod(authURL);
Enumeration emParams = authParams.propertyNames();
while (emParams.hasMoreElements()) {
  String paramName = (String) emParams.nextElement();
  String paramValue = authParams.getProperty(paramName);
     postMethod.addParameter(paramName, paramValue);
}

intResult = httpClient.executeMethod(postMethod);
postMethod.releaseConnection();
ins.close();

When executeMethod(postMethod) is executed, I get an SSLHandshakeException, CertPathBuilderException, and so on.

What can I do to remedy this? I'm thinking of either accepting the certificate or just bypassing all certificate validation (as the program runs internally within a private network).

Thanks

like image 228
ms1013 Avatar asked Jan 30 '12 04:01

ms1013


1 Answers

It looks like you're using Apache HttpClient 3. If this is indeed version 3, you need to build your own SecureProtocolSocketFactory as explained in the Apache HttpClient 3 SSL guide. There is an example here.

For Apache HttpClient 4, you should be able to pass an SSLContext to the constructor to the (HttpClient) SSLSocketFactory, as described in the answers to this question (including notes regarding host name verification).

However, generally speaking, don't follow this approach. You're effectively disabling the authentication part of the SSL/TLS connection altogether by doing so, thereby making it vulnerable to MITM attacks.

You should explicitly import the server certificate in your client's trust store instead, as described in this answer.

I'm thinking of either accepting the certificate or just bypassing all certificate validation (as the program runs internally within a private network).

What you're saying is that you're willing to use SSL/TLS for encryption only within your private network because you don't trust its users enough not to look at the traffic that may go around their machines, but you're also assuming that these users won't be able to perform a MITM attack. This doesn't quite make sense. If you trust them enough, you might as well send the data in clear. If you don't, then you should implement SSL/TLS properly, including the authentication steps (certificate and host name verification).

like image 66
Bruno Avatar answered Sep 26 '22 00:09

Bruno