Search code examples
javaandroidhttpsbouncycastlekeystore

Making an HTTPS connection using URL.openConnection()


I'm trying to make an HTTPS connection to a server that has a certificate set to expire in April 2013 and uses GlobalSign as the root certificate.

HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
// urlConnection.setSSLSocketFactory(sslSocketFactory);
urlConnection.setDoOutput(true);
urlConnection.setChunkedStreamingMode(0);

// Send the POST data
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(postParamString.toString().getBytes("UTF8"));

// Read the reply
InputStream in = urlConnection.getInputStream();

As it stands, this throws javax.net.ssl.SSLHandshakeException: org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature. when getOutputStream() is called.

This same site and certificate are valid in the stock HTC web browser and desktop browsers. When I use the same code to access Google it works (but then complains about a 404 error). Various posts on StackOverflow imply that it should "just work" and others say to set up your own key store (or disable all HTTPS validation!) I assume the difference in behaviour is down to different root key stores in use (Can anyone clarify this?).

I've now tried creating a key store using bouncy castle but I can't get this to load on my device.

After exporting the certificate from Firefox, I create a key store using:

keytool.exe -import -alias onlinescoutmanager -file www.onlinescoutmanager.co.uk.crt -storetype BKS -keystore res\raw\keystore

This is then loaded and used in the application using:

InputStream stream = context.getResources().openRawResource(R.raw.keystore);
// BKS seems to be the default but we want to be explicit
KeyStore ks = KeyStore.getInstance("BKS");
ks.load(stream, "www.onlinescoutmanager.co.uk".toCharArray());
stream.close();

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
SSLContext context2 = SSLContext.getInstance("TLS");
context2.init(null, new TrustManager[] { defaultTrustManager }, null);
sslSocketFactory = context2.getSocketFactory();

This is failing with java.io.IOException: Wrong version of key store. when keystore.Load() is called.

I have ensured I'm passing -storetype BKS, used a <=7 character keystore password, added the CA certs to the key store, and using both Bouncy Castle version 1.45 and 1.47 to create the key store with no change in the reported error message.

My environment is Eclipse Juno 4.2.1 with JRE 1.7u9b5 running on Windows 8. The device I'm testing on is an HTC sensation running stock Android 2.3. The application has a minimum SDK version of 7 and a target of 15.

If anyone can explain how to create a valid BKS key store on Windows 8 or how I can get Java to use the same key store as the browser(or system?) that would be appreciated.

You can download the entire project as it was at the time of writing, and the generated keystore if required.


Solution

  • Bouncy Castle 1.47 is using different version header. Can you try 1.46 version, it should work.

    keytool -import -alias onlinescoutmanager -file www.onlinescoutmanager.co.uk.crt -storetype BKS -storepass osmosm -keystore C:/keystore -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk15on-1.46.jar