Reputation: 111
I want to do certificate pinning in android app. I am totally frustrated to understand this. Please help me
What i have:-
I don't have domain name where this certificate will be implemented. I have only IP address. I am using okHttp and retrofit in my project. I have seen many example in stack overflow to implement this.
But I am not getting some things:- 1. If i have not domain name, Is it possible to implement ssl into IP address? 2. what will pins field contains, i don't have any private key. where will i found the private key(sha256/XXXXXXXXXX)?
Retrofit provideRetrofit(OkHttpClient okHttpClient, Gson gson) {
CertificatePinner certPinner = new CertificatePinner.Builder()
.add("patternField","pins")
.build();
OkHttpClient okHttpClientForPinning = new OkHttpClient.Builder()
.certificatePinner(certPinner)
.build();
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
try {
clientBuilder.sslSocketFactory(getSSLConfig(context).getSocketFactory());
}
catch (Exception e) {
e.printStackTrace();
}
clientBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
boolean value = true;
//TODO:Some logic to verify your host and set value
return value;
}
});
return new Retrofit.Builder()
.baseUrl(backendUrl)
.client(okHttpClient)
.client(okHttpClientForPinning)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
Upvotes: 4
Views: 13202
Reputation: 126
Certificate pins in android using Retrofit and okHttp can be implemented like following. Replace the <PIN>
with your sha256 key
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("<SERVER_URL>", "sha256/<PIN>")
.build();
OkHttpClient client = httpBuilder
.certificatePinner(certificatePinner)
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl("<BASE_URL>")
.addConverterFactory(GsonConverterFactory.create())
.build();
Upvotes: 0
Reputation: 13104
I don't have domain name where this certificate will be implemented. I have only IP address.
To get the pin for the server certificate by IP address you can try this bash script:
#!/bin/bash
# Heavily inspired on:
# * https://medium.com/@appmattus/android-security-ssl-pinning-1db8acb6621e#ecea
set -eu
Main()
{
local domain="${1? Missing domain name to extract and hash the certificate public key !!!}"
local domain="${domain##*://}"
local certs=$( openssl s_client -servername "${domain}" -host "${domain}" -port 443 -showcerts </dev/null 2>/dev/null | sed -n '/Certificate chain/,/Server certificate/p' )
local rest=$certs
while [[ "$rest" =~ '-----BEGIN CERTIFICATE-----' ]]; do
cert="${rest%%-----END CERTIFICATE-----*}-----END CERTIFICATE-----"
rest=${rest#*-----END CERTIFICATE-----}
local certificate_name="$( echo "$cert" | grep 's:' | sed 's/.*s:\(.*\)/\1/' )"
if [ -n "${certificate_name}" ]; then
printf "\nCERTIFICATE NAME:\n\n${certificate_name} \n\n"
fi
printf "\nCERTIFICATE PUBLIC KEY HASH:\n\n"
echo "$cert" |
openssl x509 -pubkey -noout |
openssl rsa -pubin -outform der 2>/dev/null |
openssl dgst -sha256 -binary |
openssl enc -base64
echo
exit 0
done
}
Main ${@}
Save the bash script to a file and invoke it like:
bash hash-certificate-public-key-from-domain.bash ip-address-here
A real life example of using it against my personal website:
╭─exadra37@exadra37-Vostro-470 ~/Developer/Approov/currency-converter-demo ‹volley-pinning-with-approov_cleanup-proguard*›
╰─➤ ./bin/hash-certificate-public-key-from-domain.bash 68.183.252.187 130 ↵
CERTIFICATE NAME:
CN = exadra37.com
CERTIFICATE PUBLIC KEY HASH:
1O0wDRM/roe6UTctDVQ5aN/ASNYsGQFVzXYhO34t5GE=
╭─exadra37@exadra37-Vostro-470 ~/Developer/Approov/currency-converter-demo ‹volley-pinning-with-approov_cleanup-proguard*›
╰─➤ ./bin/hash-certificate-public-key-from-domain.bash exadra37.com
CERTIFICATE NAME:
CN = exadra37.com
CERTIFICATE PUBLIC KEY HASH:
1O0wDRM/roe6UTctDVQ5aN/ASNYsGQFVzXYhO34t5GE=
╭─exadra37@exadra37-Vostro-470 ~/Developer/Approov/currency-converter-demo ‹volley-pinning-with-approov_cleanup-proguard*›
╰─➤ ./bin/hash-certificate-public-key-from-domain.bash https://exadra37.com
CERTIFICATE NAME:
CN = exadra37.com
CERTIFICATE PUBLIC KEY HASH:
1O0wDRM/roe6UTctDVQ5aN/ASNYsGQFVzXYhO34t5GE=
As you can see the hash for the pin 1O0wDRM/roe6UTctDVQ5aN/ASNYsGQFVzXYhO34t5GE=
is always the same.
- what will pins field contains, i don't have any private key. where will i found the private key(sha256/XXXXXXXXXX)?
The private key is private, thus if you could find it then all the security would be lost, unless you also control the server you are connecting to. Anyway you don't need it to perform Certificate pinning, just pin against the public key of the certificate, by generating an hash of it, aka a pin, like the above bash script does.
So the sha256
you are looking for is the output for the pin in the above script, aka in my examples 1O0wDRM/roe6UTctDVQ5aN/ASNYsGQFVzXYhO34t5GE=
.
An alternative is to use the free Mobile Certificate Pinning Generator online tool that will extract the public key pin with the added benefit of also generating the configuration file for you:
The resulting configuration file for Android:
While I don't know how to help you with Retrofit I can show you a simpler way of implementing pinning.
Nowadays for Android a simpler way exists, and I describe it in my blog post Securing HTTPS With Certificate Pinning, where you can learn that it can be done just by adding the certificate pin to the network security config file, or if you need to support below API level 24, then you may want to use the TrustKit package in conjunction with the file.
Example network_security_config.xml with TrustKit:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!-- Official Android N API -->
<!--https://android-developers.googleblog.com/2016/07/changes-to-trusted-certificate.html-->
<domain-config>
<domain>currency-converter-demo.pdm.approov.io</domain>
<trust-anchors>
<!--<certificates src="user" />-->
<certificates src="system" />
</trust-anchors>
<pin-set>
<!-- Pin for: currency-converter-demo.pdm.approov.io -->
<pin digest="SHA-256">qXHiE7hFX2Kj4ZCtnr8u8yffl8w9CTv6kE0U5j0o1XY=</pin>
<!-- Backup Pin for: currency-converter-demo.pdm.approov.io -->
<pin digest="SHA-256">47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=</pin>
</pin-set>
<!-- TrustKit Android API -->
<!-- enforce pinning validation -->
<trustkit-config enforcePinning="true" disableDefaultReportUri="true">
<!-- Add a reporting URL for pin validation reports -->
<report-uri>https://report.domain</report-uri>
</trustkit-config>
</domain-config>
</network-security-config>
Upvotes: 10