In the project, we need a SSL socket communication between Android and a Java server.
Here is some summaries,
- Basics of SSL protocol
SSL 1.0, never publicly released
SSL 2.0, 1995, deprecated in 2011
SSL 3.0, 1996, deprecated in June 2015
TLS 1.0, January, 1999, an upgrade of SSL 3.0
TLS 1.1, April, 2006
TLS 1.2, August 2008
TLS 1.3, still draft in today, Feb 2,2016.
The handshake between Client and Server works as follow:
- Client sends message ClientHello, initialize connection request.
- Server sends message ServerHello, answer request.
- Server exchange ServerKey, sends the public key.
- Server HelloDone.
- Client exchange ClientKey, creates a session private key using Server’s public key, sends the private key to Server.
- Client change CipherSpec, informs Server to change the encryption algorithm.
- Client finish.
- Server change CipherSpec, confirms that the encryption algorithm changed.
- Server finish.
In the protocol above, only Client needs to verify the Server’s certificate, Server doesn’t need to verify the certificate of the Client, which is called One Way Handshake.
Next, we create the self-signed certificate, we use the keytool provided by Java, OpenSSL can also do this.
We have the following things to do:
- Create self-signed certificate on the server side.
- Create client side(Android) keystore (BKS)
- Export the certificate from the server.
- Import the certificate to the Android.
Note, One Way Handshake means only Client need to verify Server’s certificate.
1
keytool -genkey -v -alias serverKey -keyalg RSA -keystore ./server.ks -storetype JKS -storepass server -keypass 1234
1
keytool -genkey -alias androidKey -keyalg RSA -keystore ./client.bks -storetype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.53.jar -storepass client -keypass 4321
Note, Android KeyStore use the BouncyCastle(BKS) format, which is different from the Jave platform(JKS), that’s why we indicate the -providerclass and -providerpath, and google for the latest .jar.
1
keytool -export -alias serverKey -keystore ./server_ks -file serverKey.cer -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.53.jar
Well, in case, we just indicate the -providerclass and -providerpath again. And the terminal may require the passcode of the keystore, use -storepass server in the first step.
1
keytool -import -alias serverKey -file serverKey.cer -keystore ./client.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.53.jar
Till here, we have done the preparation, next we need to programm it on the Android side and server side respectively.
In Android, there are two keystores, the one in the Android itself, the other one with the self-signed certificate, which we need to load into our App.
I first create a KeyStore
instance and initialize the keystore with the self-signed certificate, store.load(truststoreFile, "314durham".toCharArray());
.
Next, I initialize the TrustManagerFactory
using the KeyStore that sjust loaded.
Then, I create SSLContext
and initialize it using the local KeyStore and the TrustManagerFactory
. context.init(keyManager.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
.
From Oracle API document,
1 | public final void init(KeyManager[] km, |
In Android 4.4 and 5.0, context.init(null, tmf.getTrustManagers(), new SecureRandom());
works, but in Android 6.0, the first parameter can’t be null
, or there will be some SSL Handshake
exceptions.
1 | public static SSLSocket getSSLSocket(String ipAddr, int portNum, String keystorePath, boolean allowAllHosts) throws Exception { |