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.
![](https://www.droidcon.com/wp-content/uploads/2024/07/1_DsnzTFvFp6jMNOnqjBXDMQ.webp)
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
![](https://www.droidcon.com/wp-content/uploads/2024/07/0_pAqKJw551sipo_zx-1.webp)
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
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