Certificate pinning
Compares a certificate stored in the mobile app as being the same certificate presented by the web server that provides the HTTPS connection.
Please note: CertificatePinner can not be used to pin self-signed certificate if such certificate is not accepted by TrustManager.
This will pin the connections for a host to the public key of a certificate.
NetworkHandler.sharedInstance().setCertificatePinner(new CertificatePinner.Builder()
.add("yourhost", "sha256/public_key_of_certificate")
.build());
Asterisk * is only permitted in the left-most domain name label and must be the only character in that label, e.g. *.ibm.com
is permitted, while *verify.ibm.com
is not.
Self-signed certificates
This section applies to SSL settings for connections only to IBM Security Access Manager appliances. It will not have any effect on connections to IBM Security Verify.
If you use self-signed certificates, you additionally need to customize SSLContext, HostnameVerifier and TrustManager.
Downloading the certificate
Working with a development server, the following is the easiest way to download a certificate chain:
# for DER:
openssl s_client -connect <host>:<port> -showcerts 2>/dev/null </dev/null | openssl x509 -inform pem -outform der -out <certificate-name>.der
# for PEM:
openssl s_client -connect <host>:<port> -showcerts 2>/dev/null </dev/null | openssl x509 -inform pem -outform pem -out <certificate-name>.pem
Customize SSL settings
This sample overrides the SSL settings, so that the certificate provided by the server is accepted for this connection.
private final String certificatePath = "my-server-certificate.crt";
private final String expectedHostname = "my-server.com";
TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
};
HostnameVerifier makeHostnameVerifier(String expectedHostname) {
return new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return hostname.equalsIgnoreCase(expectedHostname);
}
};
}
SSLContext makeSslContext(String filename) {
InputStream caInput = null;
SSLContext context = null;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
caInput = new BufferedInputStream(in);
final Certificate ca;
ca = cf.generateCertificate(caInput);
Log.d("Certificate pinning", "makeSslContext: ca=" + ((X509Certificate) ca).getSubjectDN());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("our trusted CA", ca);
TrustManager customTrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
throw new CertificateException("This doesn't need to ever succeed");
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
byte[] found = chain[0].getEncoded();
byte[] wanted = ca.getEncoded();
if (!Arrays.equals(found, wanted)) {
throw new CertificateException("Presented certificate didn't match pinned certificate");
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] {customTrustManager}, null);
} catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | IOException | KeyManagementException e) {
e.printStackTrace();
} finally {
if (caInput != null) try { caInput.close(); }
catch (IOException e) {
e.printStackTrace();
}
}
return context;
}
NetworkHandler.sharedInstance.setOnPremiseSslParameter(makeSslContext(certificatePath), tm, makeHostnameVerifier(expectedHostname));
Allow all connections
The SDK provides implementations that basically turns off SSL checks. Be careful if you use them!
NetworkHandler.sharedInstance().setOnPremiseSslParameter(
NetworkHandler.sharedInstance().getSslContextTrustAll(),
NetworkHandler.sharedInstance().getTrustManager(),
NetworkHandler.sharedInstance().getHostnameVerifierAcceptAll());