What is SSL ?
SSL stands for Secure Socket Layer. It is a protocol for establishing secure data transfer between networked computers or servers.
Need For SSL-
Many developers assume that using HTTPS in a network layer is enough to be sure that user data transfer will be fully secured and not compromised by a Man-in-the-Middle (MitM) attack. In most cases that is true, but not always.
This is why to secure information passed by a browser, like a customer’s credit card number or password to a web server, most of the applications like an online store or online banking application uses SSL to prevent Man-in-the-Middle.
Android SSL Pinning–
There multiple ways we can perform SSL pinning in android
1- Certificate pinning
2-Public key pinning
3- SPKI (SubjectPublicKeyInfo) pinning
Here we will focus our attention on Public key pinning as it is the most recommended way for safe SSL pinning operations.
To implement the pinning you need to know your certificates SPKI data.
we can retrieve it using OpenSSL.
Pinning on Android N(API 24) and above-:
If minimum SDK is Android N then the implementation is very simple as Android has a new API in Android SDK from API 24 onwards the Network Security Configuration.
you just need to enter an XML configuration file that defines the pins you require in our AndroidManifest.xml file.
<?xml version=”1.0″ encoding=”utf-8″?>
<network-security-config>
<domain-config>
<domain includeSubdomains=”true”>xyz.com</domain>
<pin-set>
<pin digest=”SHA-256″>6jj6tz+scE+XW+mlai6ZipDfFWn1dqvfLG+nU7tq1V8=</pin>
<pin digest=”SHA-256″>LLh6dUR9y6Kka30RrAn7fKbQG/uEtLMkBgFF2Fuihg=</pin>
</pin-set>
</domain-config>
</network-security-config>
Declaration of Configuration file in AndroidManifest.xml:-
<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
………………
…………….
<application
android:name=”.application.XYZApplication”
android:allowBackup=”false”
android:icon=”@mipmap/app_launcher”
android:label=”@string/app_name”
android:supportsRtl=”true”
android:theme=”@style/AppTheme”
android:networkSecurityConfig=”@xml/network_security_config”
tools:replace=”android:allowBackup,android:icon”>
</application>
</manifest>
if you want to use network libraries for pinning we can do the following
Pinning with Retrofit-
Pining with Retrofit is easy being built on top of OkHttp.
CertificatePinner certPinner = new CertificatePinner.Builder()
.add(“xyz.com”,
“sha256/8Rw90Ej3Ttt8RRkrg+WYDS9n7IS03bk5bjP/UXPtaY8=”)
.build();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.certificatePinner(certPinner)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(“https://xyz.com”)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
Pinning with HttpUrlConnection-
if you are still using HttpURlconnection consider upgrading it to some other network libraries.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.List; import java.util.Locale; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; public class WebServiceManager { private static String TAG = "WebServiceManager"; private static String requestHttpsServer(String targetUrl, String jsonRequestString, int httpMethod, int readTimeOutType) throws IELTSException { String jsonResponseString = null; HttpsURLConnection httpsUrlConnection = null; URL url; try { url = new URL(AppConstants.BASE_URL + targetUrl); httpsUrlConnection = (HttpsURLConnection) url.openConnection(); httpsUrlConnection.setConnectTimeout(AppConstants.TIME_CONNECTION_TIMEOUT); if (readTimeOutType == AppConstants.READ_TIMEOUT_SMALL) { httpsUrlConnection.setReadTimeout(AppConstants.TIME_CONNECTION_DATA_WAIT_TIMEOUT_SMALL); } else { httpsUrlConnection.setReadTimeout(AppConstants.TIME_CONNECTION_DATA_WAIT_TIMEOUT_LARGE); } // applying SSL pinning // getting SPKI keys for validation String[] spki = CachingManager.getAppContext().getResources().getStringArray(R.array.spki); List validPins = Arrays.asList(spki); // getting trust manager X509TrustManagerExtensions trustManagerExt = prepTrustManager(); // validating pins for ssl pinning validatePinning(trustManagerExt, httpsUrlConnection, validPins); ...................... ................... return jsonResponseString; } // this method validate the pins return by the server with the public key pin and throw an exception private static void validatePinning(X509TrustManagerExtensions trustManagerExt, HttpsURLConnection conn, List validPins) throws SSLException { String certChainMsg = ""; try { MessageDigest md = MessageDigest.getInstance("SHA-256"); // getting list of trust chain List trustedChain = trustedChain(trustManagerExt, conn); // validating pins for both side for (X509Certificate cert : trustedChain) { byte[] publicKey = cert.getPublicKey().getEncoded(); md.update(publicKey, 0, publicKey.length); String pin = Base64.encodeToString(md.digest(), Base64.NO_WRAP); certChainMsg += " sha256/" + pin + " : " + cert.getSubjectDN().toString() + "\n"; if (validPins.contains(pin)) { return; } } } catch (NoSuchAlgorithmException e) { throw new SSLException(e); } throw new SSLPeerUnverifiedException("Certificate pinning " + "failure\n Peer certificate chain:\n" + certChainMsg); } // extracting the X509 certificates chains for server end private static List trustedChain(X509TrustManagerExtensions trustManagerExt, HttpsURLConnection conn) throws SSLException { Certificate[] serverCerts = conn.getServerCertificates(); X509Certificate[] untrustedCerts = Arrays.copyOf(serverCerts, serverCerts.length, X509Certificate[].class); String host = conn.getURL().getHost(); try { return trustManagerExt.checkServerTrusted(untrustedCerts, "RSA", host); } catch (CertificateException e) { throw new SSLException(e); } } // prepare TrustManagers private static X509TrustManagerExtensions prepTrustManager() { // creating trust manager to get X509 trust manager TrustManagerFactory trustManagerFactory = null; try { trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init((KeyStore) null); } catch (NoSuchAlgorithmException | KeyStoreException e) { Log.e(TAG, e.toString()); } // Find first X509TrustManager in the TrustManagerFactory X509TrustManager x509TrustManager = null; for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) { if (trustManager instanceof X509TrustManager) { x509TrustManager = (X509TrustManager) trustManager; break; } } return new X509TrustManagerExtensions(x509TrustManager); } }