Blog Infos
Author
Published
Topics
, , , ,
Published

In today’s digital age, securing user data and maintaining the integrity of communications are of utmost importance for mobile applications. One critical aspect of ensuring secure communication between your Android app and the backend server is the use of SSL/TLS. While SSL/TLS provides a secure channel, it alone is not foolproof against all types of attacks, especially Man-in-the-Middle (MITM) attacks. This is where SSL Pinning comes into play.

Ssl certificate icons created by setiawanap — Flaticon

 

What is SSL?

SSL (Secure Sockets Layer) and its successor TLS (Transport Layer Security) are cryptographic protocols designed to provide secure communication over a computer network. They are widely used to secure connections between clients and servers, ensuring that data transmitted over these connections is encrypted and safe from eavesdropping and tampering.

What is SSL Pinning?

SSL Pinning is a technique that adds an extra layer of security to SSL/TLS by ensuring that the client (your Android app) only accepts a specific server certificate or public key. By pinning the server’s certificate or public key within the app, you can prevent attackers from intercepting and manipulating the communication, even if they manage to compromise the network security. This significantly reduces the risk of MITM attacks, where an attacker could otherwise impersonate your server and intercept sensitive information.

Threats and Risks
MITM Attack Scheme from WaveMaker

Despite SSL/TLS offering a robust security mechanism, there are scenarios where attackers can exploit weaknesses in the certificate validation process. For instance, if a trusted Certificate Authority (CA) is compromised, attackers could issue fraudulent certificates that appear legitimate. MITM attacks leverage such vulnerabilities to intercept and potentially alter the data being exchanged between the client and the server. SSL Pinning mitigates these risks by ensuring that the app communicates only with a server presenting the pinned certificate or public key.

In this article, we will explore how to implement SSL Pinning in your Android applications using two popular networking libraries: OkHttp and Retrofit. By the end of this guide, you will have a clear understanding of how to enhance your app’s security and protect your users’ data from sophisticated attacks.

This introduction sets the stage by explaining the importance of SSL/TLS, introducing SSL Pinning, and highlighting the threats it mitigates. It also provides a clear overview of what the reader can expect to learn from the article.

First of all, we should get our SHA-256 certificate fingerprint by using following command:

echo | openssl s_client -connect api.github.com:443 2>/dev/null | openssl x509 -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64

And the output should be like this:

47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=

Then by using following code, we create a certificate pinner for our OkHttp client:

val hostName = "api.github.com"
val certificatePinner = CertificatePinner.Builder()
    .add(hostName, "sha256/lmo8/KPXoMsxI+J9hY+ibNm2r0IYChmOsF9BxD74PVc=")
    .build()

Then use chain method of the OkHttp.Builder,

.certificatePinner(certificatePinner)

After that, we can create our model classes, repositories, use cases and make service calls.

My view model usage is like this to see the error in user interface — just for testing purposes — :

Let’s run the app and see the result:

If we give a wrong fingerprint, let’s see:

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

No results found.

Jobs

Yes! We got an error. The scenario is this, when somebody else is wanted to monitor or use our webservices, we wanted to block this action.

Bonus

What if we have more than one endpoints that are not suitable with the same SSL configuration or doesn’t need an SSL configuration?

I use this repository for this actually, you know, we have dependency injection and we are using Hilt for it.

It has an annotation called “Named”, it keeps instances and returns by this Named key.

Just an example:

These are Retrofit providers:

@Provides
@Singleton
@Named("Non-SSL")
fun provideNonSSLRetrofit(@Named("Non-SSL") okHttpClient: OkHttpClient): Retrofit {
    return Retrofit.Builder()
        .client(okHttpClient)
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
}
@Provides
@Singleton
@Named("SSL")
fun provideSSLRetrofit(@Named("SSL") okHttpClient: OkHttpClient): Retrofit {
    return Retrofit.Builder()
        .client(okHttpClient)
        .baseUrl(BuildConfig.GITHUB_BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
}

And the OkHttpClient providers:

@Provides
@Singleton
@Named("SSL")
fun provideSSLOkHttpClient(chuckerInterceptor: ChuckerInterceptor): OkHttpClient {
    val hostName = "api.github.com"
    val certificatePinner = CertificatePinner.Builder()
        .add(hostName, "sha256/lmo8/KPXoMsxI+J9hY+ibNm2r0IYChmOsF9BxD74PVc=")
        .build()
    return OkHttpClient.Builder()
        .addInterceptor(chuckerInterceptor)
        .certificatePinner(certificatePinner)
        .build()
}

@Provides
@Singleton
@Named("Non-SSL")
fun provideNonSSLOkHttpClient(chuckerInterceptor: ChuckerInterceptor): OkHttpClient {
    return OkHttpClient.Builder()
        .addInterceptor(chuckerInterceptor)
        .build()
}

We see that there are two instances, one is for GitHub, which is we wanted to do SSL Pinning, one is the old Todo API. Just create provider methods for configurations. Than use like this:

@Provides
@Singleton
fun provideApiService(@Named("Non-SSL") retrofit: Retrofit): ApiService {
    return retrofit.create(ApiService::class.java)
}
@Provides
@Singleton
fun provideGitHubApiService(@Named("SSL") retrofit: Retrofit): GitHubApiService {
    return retrofit.create(GitHubApiService::class.java)
}

I use my playground repository for SSL pinning example, if you want, just go to the “feature/ssl” branch for full code.

What if, we use Ktor?

It’s very simple that, you can use certificate pinner like Retrofit implementation.

object HttpClient {
    val client = HttpClient(OkHttp) {
        engine {
            config {
                val certificatePinner = CertificatePinner.Builder()
                    .add("api.github.com", "sha256/lmo8/KPXoMsxI+J9hY+ibNm2r0IYChmOsF9BxD74PVc=")
                    .build()
                certificatePinner(certificatePinner)
            }
        }
    }
}

I hope, it’s helpful, happy coding!

This article is previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
This tutorial is the second part of the series. It’ll be focussed on developing…
READ MORE
blog
A few weeks ago I started with a simple question — how to work…
READ MORE
blog
Using annotations in Kotlin has some nuances that are useful to know
READ MORE
blog
One of the latest trends in UI design is blurring the background content behind the foreground elements. This creates a sense of depth, transparency, and focus,…
READ MORE
Menu