Android and SSL

Much like iOS, Android communication with a secure server is no different than with an open server; the only difference is the protocol specified. And again, if you're part of an organization that has a trusted certificate, the only step required is to change the URL in LocationLoader.java from http to https.

Predictably, Android will reject self-signed certificates by default. However, the process for overriding this behavior differs from the approach with iOS. To start off, I'll introduce a new class of general-purpose helper methods in a class named MiscUtils.java:

  1. package com.crowleyworks.futilefishing.util;
  2.  
  3. import java.security.SecureRandom;
  4. import java.security.cert.CertificateException;
  5. import java.security.cert.X509Certificate;
  6.  
  7. import javax.net.ssl.HostnameVerifier;
  8. import javax.net.ssl.HttpsURLConnection;
  9. import javax.net.ssl.SSLContext;
  10. import javax.net.ssl.SSLSession;
  11. import javax.net.ssl.X509TrustManager;
  12.  
  13. import com.crowleyworks.futilefishing.Main;
  14.  
  15. import android.app.AlertDialog;
  16. import android.content.Context;
  17. import android.content.DialogInterface;
  18. import android.util.Log;
  19.  
  20. public class MiscUtils {
  21. private static String trustedHost = "192.168.1.2";
  22. public static void trustSSL() {
  23. try {
  24. HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
  25. @Override
  26. public boolean verify(String hostname, SSLSession session) {
  27. Log.i(Main.TAG, "Verifying hostname: " + hostname);
  28. return hostname.equals(trustedHost);
  29. }
  30. });
  31.  
  32. SSLContext context = SSLContext.getInstance("TLS");
  33. context.init(null, new X509TrustManager[]{new X509TrustManager() {
  34.  
  35. @Override
  36. public void checkClientTrusted(X509Certificate[] chain, String authType)
  37.  
  38. @Override
  39. public void checkServerTrusted(X509Certificate[] chain, String authType)
  40.  
  41. @Override
  42. public X509Certificate[] getAcceptedIssuers() {
  43. return new X509Certificate[0];
  44. }
  45.  
  46. }}, new SecureRandom());
  47.  
  48. HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
  49.  
  50. } catch(Exception e) {
  51. e.printStackTrace();
  52. }
  53. }
  54.  
  55. public static void showAlert(Context context, String title, String msg) {
  56. AlertDialog.Builder ab = new AlertDialog.Builder(context);
  57. ab.setTitle(title);
  58. ab.setMessage(msg);
  59. ab.setCancelable(false);
  60. ab.setPositiveButton("OK", new DialogInterface.OnClickListener() {
  61.  
  62. @Override
  63. public void onClick(DialogInterface dialog, int which) {
  64. dialog.cancel();
  65. }
  66. });
  67. AlertDialog ad = ab.create();
  68. ad.show();
  69. }
  70.  
  71. }

Comments:

  • Line 21: I'm specifying the server I trust. (This should not be hard-coded in the real world.)
  • Lines 24 - 30: Override the default HostnameVerifier and check for the server specified above. If it's a match, return true.
  • Lines 32 - 50: It's also necessary to define a new default SSL socket factory. This way, I'm able to intercept requests to verify host names. (You may notice that there's not much code here; just some skeleton code allowing for default behavior.)
  • Lines 57 - 71: The default UI Alert in Android is rather verbose, so I like to provide a convenience method to make things simpler.



The only change to LocationBroker.java is to change the URL protocol from http to https.

Finally, in Main.java, I add a private static property for handling SSL bypass:

  1. private static final boolean bypassSSLCheck = true;

In the onCreate() method, I add code to handle the bypass check:

  1. // ...
  2.  
  3. if (bypassSSLCheck) {
  4. MiscUtils.trustSSL();
  5. MiscUtils.showAlert(this, "SSL Warning", "SSL Certification Bypass Active");
  6. }
  7.  
  8. // ...

Note: No change to LocationBroker.java was required, because the change to the certificate authentication impacts all connections within this app. Depending on your point of view, this is either a benefit or a liability. (I tend to think it's neither - it's just different.)

It's now possible to launch the app and securely communicate with the server. This completes all of the necessary updates to both mobile platforms.