Enable SSL pinning
The Ping SDKs support SSL pinning, sometimes referred to as certificate pinning. SSL pinning is the security practice of validating the certificates presented by the server against known values.
When the SDK attempts to make an HTTPS connection to your authorization server, it first verifies that a hash of the server’s public key (obtained from the server’s SSL certificate) matches a set of hashes defined within your app. This SSL pinning reduces the chance of a man-in-the-middle (MITM) attack, improving the security of your app.
If the hash does not match, your app does not connect to the authorization server, and an error is returned instead. Note that if your public key changes, you will need to rebuild and re-release your app with the new hash included.
Get a hash of the public key from your server
To enable SSL pinning you need a hash of your server’s public key. You can use the openssl
tool to extract this from your server’s SSL certificate and create the hash value.
In the following command, replace <tenant-env-fqdn>
with the fully-qualified domain name of your server, for example, my-company.forgeblocks.com
:
echo | openssl s_client -servername <tenant-env-fqdn> -connect <tenant-env-fqdn>:443 | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
The command outputs a hash of the public key extracted from the certificate:
S4kZuhQQ1DPcXBSWFQXD0gG+UW7usdbVx6roNWpRl65I=
Use this value in the next steps to configure SSL pinning.
Configure SSL pinning in Android
To enable SSL pinning in the Ping SDK for Android, add the hash of the public keys for any PingAM authorization servers your application will contact to your app’s configuration.
Add the hashes to an array named forgerock_ssl_pinning_public_key_hashes
in your strings.xml
file:
<string-array name="forgerock_ssl_pinning_public_key_hashes">
<item>S4kZuhQQ1DPcXBSWFQXD0gG+UW7usdbVx6roNWpRl65I=</item>
</string-array>
If the public key you use to obtain SSL certificates for the PingAM servers change, update the strings.xml
file with the new hash and re-release your app.
You can also update this property programmatically by using dynamic configuration. |
Override default implementation of SSL pinning for Android
You can override how the Ping SDK for Android performs SSL pinning by registering your own implementation.
To override the default SSL pinning, you create your own implementation of checkServerTrusted()
:
try {
final TrustManager myCustomTrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
// Provide custom SSL Pinning handling
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { myCustomTrustManager }, new java.security.SecureRandom());
Config.getInstance().reset();
Config.getInstance().init(this, null);
Config.getInstance().setBuildSteps(Collections.singletonList(builder1 -> {
builder1.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) myCustomTrustManager);
builder1.hostnameVerifier((s, sslSession) -> true);
}));
} catch (NoSuchAlgorithmException | KeyManagementException e) {
runOnUiThread(() -> content.setText(e.getMessage()));
}
Alternatively, you can use dynamic configuration to override the SDK’s SSL pinning functionality:
val myCustomTrustManager: TrustManager = object : X509TrustManager {
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
// Provide custom SSL Pinning handling
}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
}
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, arrayOf(myCustomTrustManager), SecureRandom())
val option = FROptionsBuilder.build {
server {
forgerock_url = "https://custom.example.com"
forgerock_realm = "prod"
}
sslPinning {
buildSteps = listOf(object: BuildStep<OkHttpClient.Builder> {
override fun build(builder1: OkHttpClient.Builder) {
builder1.sslSocketFactory(
sslContext.socketFactory,
myCustomTrustManager as X509TrustManager
)
builder1.hostnameVerifier { s, sslSession -> true }
}
})
forgerock_ssl_pinning_public_key_hashes = emptyList()
}
}
Configure SSL pinning in iOS
To enable SSL pinning in the Ping SDK for iOS, add the hash of the public keys for any PingAM authorization servers your application will contact to your app’s configuration.
Add the hashes to an array named forgerock_ssl_pinning_public_key_hashes
in your FRAuthConfig.plist
file:
<key>forgerock_ssl_pinning_public_key_hashes</key>
<array>
<string>S4kZuhQQ1DPcXBSWFQXD0gG+UW7usdbVx6roNWpRl65I=</string>
</array>
If the public key you use to obtain SSL certificates for the PingAM servers change, update the FRAuthConfig.plist
file with the new hash and re-release your app.
You can also update this property programmatically by using dynamic configuration. |
Override default implementation of SSL pinning for iOS
You can override how the Ping SDK for iOS performs SSL pinning by registering your own implementation.
To override the default SSL pinning, create a new CustomPinningHandler
subclass of the default FRURLSessionSSLPinningHandler
class. Override the implementation of the urlSession
functions:
class CustomPinningHandler: FRURLSessionSSLPinningHandler {
override func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
// Provide Custom SSL Pinning handling
}
override func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
// Provide Custom SSL Pinning handling
}
}
Add your new custom handler as part of the configuration:
let customPinningHandler = CustomPinningHandler(frSecurityConfiguration: nil)
RestClient.shared.setURLSessionConfiguration(config: nil, handler: customPinningHandler)