2

I tried to use client certificate authentication with URLConnection on java 6u45 and it returns me 400 HTTP code, but when I build the same with java 7u25 it works fine and gives back OK 200.

When I tried Apache HttpPost it gives 400 error for both 6u45 as well as 7u25.

C:\java\test>"C:\Program Files (x86)\Java\jdk1.6.0_45\bin\javac" Test.java -cp ".;lib/*"

C:\java\test>"C:\Program Files (x86)\Java\jdk1.6.0_45\bin\java" -cp ".;lib/*" Test
Response Code 1: 400
Response Code 2: 400

C:\java\test>"C:\Program Files (x86)\Java\jdk1.7.0_25\bin\javac" Test.java -cp ".;lib/*"

C:\java\test>"C:\Program Files (x86)\Java\jdk1.7.0_25\bin\java" -cp ".;lib/*" Test
Response Code 1: 200
Response Code 2: 400

I believe that it is quite easy to make it work on java 6 at least with Apache HttpPost, but I do something wrong.

My test code is as follows:

import java.io.*;
import java.net.*;
import java.security.*;
import javax.net.ssl.*;
import org.apache.http.*;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.*;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.BasicClientConnectionManager;
import org.apache.http.params.*;
import org.apache.http.entity.StringEntity;

public class Test {
    public static void main(String[] args) {
        String keyStorePath = "C:\\java\\keys\\client_certificate.p12";
        String keyStorePass = "someKeyStorePass";
        String trustStorePath = "C:\\java\\keys\\truststore.jks";
        String trustStorePass = "someTrustStorePath";
        String postData = "<request></request>";
        String url = "https://api.some-url.com/v2";

        SSLContext sslContext = GetSSLContext(keyStorePath, keyStorePass, trustStorePath, trustStorePass);
        PostData1(postData, url, sslContext);

        SchemeRegistry schemeRegistry = GetSchemeRegistry(keyStorePath, keyStorePass, trustStorePath, trustStorePass);
        PostData2(postData, url, schemeRegistry);
    }

    public static void PostData1(String dataToPost, String serviceUrl, SSLContext sslContext) {
        try {
            URL url = new URL(serviceUrl);

            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
            HttpsURLConnection urlConn = (HttpsURLConnection)url.openConnection();
            urlConn.setDoOutput(true);
            urlConn.setDoInput(true);
            urlConn.setUseCaches(false);
            urlConn.setRequestMethod("POST");
            urlConn.setRequestProperty("Content-Type", "application/xml");
            urlConn.connect();

            PrintWriter out = new PrintWriter(urlConn.getOutputStream());
            out.println(dataToPost);
            out.close();

            System.out.println("Response Code 1: " + urlConn.getResponseCode());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void PostData2(String dataToPost, String serviceUrl, SchemeRegistry schemeRegistry) {
        try {
            HttpParams httpParams = new BasicHttpParams();
            DefaultHttpClient httpClient = new DefaultHttpClient(new BasicClientConnectionManager(schemeRegistry), httpParams);

            HttpPost httpPost = new HttpPost(serviceUrl);

            HttpEntity lEntity = new StringEntity(dataToPost, "UTF-8");
            httpPost.setEntity(lEntity);

            HttpResponse response = httpClient.execute(httpPost);

            try {
                System.out.println("Response Code 2: " + response.getStatusLine().getStatusCode());
            } finally {
                httpPost.releaseConnection();
            }

            httpClient.getConnectionManager().shutdown();

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static SSLContext GetSSLContext(String clientStorePath, String clientStorePass, String trustStorePath, String trustStorePass) {
        SSLContext sslContext = null;

        try {
            KeyStore clientStore = KeyStore.getInstance("PKCS12");
            clientStore.load(new FileInputStream(clientStorePath), clientStorePass.toCharArray());

            KeyManagerFactory kmf =KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(clientStore, clientStorePass.toCharArray());
            KeyManager[] kms = kmf.getKeyManagers();

            KeyStore trustStore = KeyStore.getInstance("JKS");
            trustStore.load(new FileInputStream(trustStorePath), trustStorePass.toCharArray());

            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(trustStore);
            TrustManager[] tms = tmf.getTrustManagers();

            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(kms, tms, new SecureRandom());

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return sslContext;
    }

    public static SchemeRegistry GetSchemeRegistry(String clientStorePath, String clientStorePass, String trustStorePath, String trustStorePass) {
        final SchemeRegistry schemeRegistry = new SchemeRegistry();

        try {
            KeyStore clientStore = KeyStore.getInstance("PKCS12");
            clientStore.load(new FileInputStream(clientStorePath), clientStorePass.toCharArray());

            KeyStore trustStore = KeyStore.getInstance("JKS");
            trustStore.load(new FileInputStream(trustStorePath), trustStorePass.toCharArray());

            schemeRegistry.register(new Scheme("https", 443, new SSLSocketFactory(clientStore, clientStorePass, trustStore)));

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return schemeRegistry;
    }
}
2
  • Surely you are also setting javax.net.ssl.keyStoreType to "pkcs11"? Otherwise I don't see how what is apparently a PCKS#12 keystore file can work at all. Or else the server isn't actually asking for client authentication at all. Commented Aug 1, 2013 at 7:28
  • 1
    I just tried to add -Djavax.net.ssl.trustStore="pkcs11" but get the same result. I'm sure, that the server requires client certificate. Commented Aug 1, 2013 at 9:53

1 Answer 1

1

After a deep investigation of HTTPS protocol traffic I found out that Java 6 doesn't support SNI and this feature was added only in Java 7.

My Nginx server have several HTTPS VirtualHosts on the same IP and because of Java 6 doesn't pass the exact hostname the default host was returned.

To resolve this issue you just need to mark your VirtualHost with client side authentication as the default one:

listen xxx.xxx.xxx.xxx:443 default;
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.