One of the most important things in mobile development is secure communication, especially between the app and its backend server. Currently, the most common architecture of web services is REST based on HTTP. The best protection method for this model of communication is the TLS/SSL standard. It can be combined with the HTTP protocol to create an encrypted variant called HTTPs. HTTPs ensures safe, encrypted communication channels between client apps and the backend server. Moreover, implementing this security feature is very simple on Android. You just need to watch out for some common pitfalls.
SolutionTo avoid this exploit, developers should implement Certificate Pinning. It’s a method that depends on server certificate verification on the client side. This verification requires the server certificate or its fingerprint to be previously known to the mobile app. When establishing a connection with the server, the app should compare the fingerprint with a certificate from the remote server. If the fingerprints are identical, then the connection is valid and the data transfer can proceed. If the fingerprints are not identical, then the app should reject the connection immediately, as it’s compromised. The following 3 methods are the most popular ways to implement Certificate Pinning in Android apps.
val resourceStream = resources.openRawResource(R.raw.demo_cert) val keyStoreType = KeyStore.getDefaultType() val keyStore = KeyStore.getInstance(keyStoreType) keyStore.load(resourceStream, null)
val trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm() val trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm) trustManagerFactory.init(keyStore)
val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, trustManagerFactory.trustManagers, null) val url = URL("http://www.example.com/") val urlConnection = url.openConnection() as HttpsURLConnection urlConnection.sslSocketFactory = sslContext.socketFactory
val certificatePinner = CertificatePinner.Builder() .add( "www.example.com", "sha256/ZC3lTYTDBJQVf1P2V7+fibTqbIsWNR/X7CWNVW+CEEA=" ).build() val okHttpClient = OkHttpClient.Builder() .certificatePinner(certificatePinner) .build()
You can add multiple fingerprints for different domains. Multiple fingerprints will also make your app more flexible. You can add all fingerprints from the certification path. You can also add additional certificates if the old ones are going to expire soon. Fingerprints can be retrieved directly from the certificate. You can also import the certificate file to the resources folder, like in TrustManager case. This time you need to manually write a class that will extract the fingerprint from the file. You can also use Peer certificate extractor to do that for you.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="co.netguru.demoapp"> <application android:networkSecurityConfig="@xml/network_security_config"> ... </application> </manifest
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">example.com</domain> <pin-set> <pin digest="SHA-256">ZC3lTYTDBJQVf1P2V7+fibTqbIsWNR/X7CWNVW+CEEA=</pin> <pin digest="SHA-256">GUAL5bejH7czkXcAeJ0vCiRxwMnVBsDlBMBsFtfLF8A=</pin> </pin-set> </domain-config> </network-security-config>