I'm reading about certificate pinning on Android and I'm confused. I'm not using okhttp or retrofit so I have to do it manually. There is a tutorial here: https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#Android where they are adding the certificate to list of trusted certificates. But there is also another tutorial when we're checking base64 of sha256 of the certificate installed on the server: https://medium.com/@appmattus/android-security-ssl-pinning-1db8acb6621e Which approach is the correct one? Why can't we just receive sha256 from the server in the header as browsers do and store it somewhere?
Certificate Pinning on Android with Robospice
Asked Answered
I would recommend this
https://www.paypal-engineering.com/2015/10/14/key-pinning-in-mobile-applications/
Android Method
The simplest approach is to use a JSEE-based method as shown below. This is the recommended approach for Android. The method’s input arguments are an HTTPS connection and a set of valid pins for the targeted URL.
private boolean validatePinning(HttpsURLConnection conn, Set<String> validPins) {
try {
Certificate[] certs = conn.getServerCertificates();
MessageDigest md = MessageDigest.getInstance("SHA-256");
for (Certificate cert : certs) {
X509Certificate x509Certificate = (X509Certificate) cert;
byte[] key = x509Certificate.getPublicKey().getEncoded();
md.update(key, 0, key.length);
byte[] hashBytes = md.digest();
StringBuffer hexHash = new StringBuffer();
for (int i = 0; i < hashBytes.length; i++) {
int k = 0xFF & hashBytes[i];
String tmp = (k<16)? "0" : "";
tmp += Integer.toHexString(0xFF & hashBytes[i]);
hexHash.append(tmp);
}
if (validPins.contains(hexHash.toString())) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return false;
}
The pins are declared as strings. For instance:
Declaring Key Pins
private static final Set<String> PINS = new HashSet<String>(Arrays.asList(
new String[]{
"996b510ce2380da9c738...87cb13c9ec409941",
"ba47e83b1ccf0939bb40d2...edf856ba892c06481a"}));
Leveraging the above method, here is an example showing how this can be put to use. The only relevant portion is highlighted below.
Example Using Key Pinning
protected String doInBackground(String... urls) {
try {
/** Test pinning given the target URL **/
/** for now use pre-defined endpoint URL instead or urls[0] **/
Log.i(LOG_TAG, "==> PinningTestTask launched.");
String dest = defaultEndpoint;
URL targetURL = new URL(dest);
HttpsURLConnection targetConnection = (HttpsURLConnection) targetURL.openConnection();
targetConnection.connect();
if (validatePinning(targetConnection, PINS)) {
final String updateText = "Key pinning succeded for: " + dest;
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(updateText);
}
});
} else {
final String updateText = "Key pinning failed for: " + dest;
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(updateText);
}
});
}
} catch (Exception e) {
e.printStackTrace();
final String updateText = "Key pinning failed for: " + dest + "\n" + e.toString();
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(updateText);
}
});
}
return null;
}
© 2022 - 2024 — McMap. All rights reserved.
A more elegant solution is put forward by PayPal in Key Pinning in Mobile Applications however this suffers from the security flaw discussed in An Examination of Ineffective Certificate Pinning Implementations. The code proposed checks the pins against httpsUrlConnection.getServerCertificates() which returns a list of certificate suggestions from the server and are not necessarily trusted by the device.
– Spurry