Ping SDKs

Enable SSL pinning

Applies to:

  • Ping SDK for Android

  • Ping SDK for iOS

  • Ping SDK for JavaScript

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)