DEV Community

Cover image for SSL Pinning in React Native for iOS and Android
Ajmal Hasan
Ajmal Hasan

Posted on • Edited on

SSL Pinning in React Native for iOS and Android

Securing data transmission in mobile applications is critical. One way to secure this communication is through SSL Pinning. SSL Pinning ensures that your app only communicates with your server using trusted certificates, enhancing the security against man-in-the-middle (MITM) attacks.

This guide will walk you through setting up SSL pinning in both iOS and Android in your React Native app.


Gathering Information for SSL Pinning

Step 1: Test Your SSL Certificate

Start by going to the SSL Labs SSL Test, enter your domain or api base url(like your-domain.com), and initiate a scan. The results will include details about your SSL certificate.

SSL Labs SSL Test Screenshot

Step 2: Requirements for iOS SSL Pinning

Note: iOS requires at least two public key hashes (primary and backup) to enable SSL pinning.

Two Hashes Requirement

Step 3: Copy the SHA256 Pin and Domain:

Locate and copy the Pin SHA256 hash.

Pin SHA256 Screenshot

Image description

Note:

  1. We can get SHA256 Pin from Additional Certificates (if supplied) also.
  2. Check Valid until on the page. After this the certificate will expire so you again have to add new ones and make new app builds.
  3. Copy Domain for eg: The domain for https://dev.to/ is dev.to.

Implementing SSL Pinning on iOS

Step 1: Update Your Podfile

Add TrustKit to your Podfile for SSL pinning support.

require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, '12.4'
install! 'cocoapods', :deterministic_uuids => false

target 'sslPinning' do
  config = use_native_modules!
  pod 'TrustKit' # Add TrustKit for SSL Pinning
  ...
Enter fullscreen mode Exit fullscreen mode

Run pod install to install the new dependency.

Step 2: Configure AppDelegate.mm

  1. Import TrustKit and Configure TrustKit in the application:didFinishLaunchingWithOptions: method:
...
   #import <TrustKit/TrustKit.h>
...

   - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
   {
...
     NSDictionary *trustKitConfig = @{
       kTSKSwizzleNetworkDelegates: @YES,
       kTSKPinnedDomains: @{
           @"yourdomain.com" : @{
               kTSKIncludeSubdomains: @YES,
               kTSKEnforcePinning: @YES,
               kTSKDisableDefaultReportUri: @YES,
               kTSKPublicKeyHashes : @[
                 @"<Primary SHA256 key>", // Replace with your actual SHA256 key
                 @"<Backup SHA256 key>",  // Replace with a backup key
               ],
           },
       }};
     [TrustKit initSharedInstanceWithConfiguration:trustKitConfig];
...

Enter fullscreen mode Exit fullscreen mode

Update "yourdomain.com" with your domain and replace <Primary SHA256 key> and <Backup SHA256 key> with your actual SHA256 keys.


Implementing SSL Pinning on Android

a) JAVA

Step 1: Create SSLPinningFactory.java

Inside android/app/src/main/java/com/yourappname/, create a new file named SSLPinningFactory.java and add the following code:

package com.yourappname; // Update with your package name

import com.facebook.react.modules.network.OkHttpClientFactory;
import com.facebook.react.modules.network.OkHttpClientProvider;
import okhttp3.CertificatePinner;
import okhttp3.OkHttpClient;

public class SSLPinningFactory implements OkHttpClientFactory {
   private static String hostname = "yourdomain.com"; // Update with your domain

   public OkHttpClient createNewNetworkModuleClient() {

      CertificatePinner certificatePinner = new CertificatePinner.Builder()
        .add(hostname, "sha256/<your SHA256 key>") // Replace with your SHA256 key
        .build();

      OkHttpClient.Builder clientBuilder = OkHttpClientProvider.createClientBuilder();
      return clientBuilder.certificatePinner(certificatePinner).build();
  }
}
Enter fullscreen mode Exit fullscreen mode

Note: Replace "your SHA256 key" with your actual SHA256 hash.

Step 2: Register the SSL Pinning Factory in MainApplication.java

In MainApplication.java, configure SSL Pinning by adding the following line in the onCreate method:

+ import com.facebook.react.modules.network.OkHttpClientProvider;
...

public class MainApplication extends Application implements ReactApplication {

  @Override
  public void onCreate() {
    super.onCreate();
    ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
    SoLoader.init(this, /* native exopackage */ false);
    initializeFlipper(this, getReactNativeHost().getReactInstanceManager());

 +   OkHttpClientProvider.setOkHttpClientFactory(new SSLPinningFactory()); // Register the SSLPinningFactory
  }
}
Enter fullscreen mode Exit fullscreen mode

---------------------OR----------------------

b) KOTLIN

SSLPinningFactory.kt

package com.-----

import com.facebook.react.modules.network.OkHttpClientFactory
import com.facebook.react.modules.network.OkHttpClientProvider
import okhttp3.CertificatePinner
import okhttp3.OkHttpClient

class SSLPinningFactory : OkHttpClientFactory {
    companion object {
        private const val hostname = "-----"
        private val sha256Keys = listOf(
            "sha256/------",
            "sha256/------")
    }
    override fun createNewNetworkModuleClient(): OkHttpClient {
        val certificatePinnerBuilder = CertificatePinner.Builder()
        for (key in sha256Keys) {
            certificatePinnerBuilder.add(hostname, key)
        }
        val certificatePinner = certificatePinnerBuilder.build()
        val clientBuilder = OkHttpClientProvider.createClientBuilder()
        return clientBuilder.certificatePinner(certificatePinner).build()
    }
}
Enter fullscreen mode Exit fullscreen mode

MainApplication.kt

...
+ import com.facebook.react.modules.network.OkHttpClientProvider // Import OkHttpClientProvider

class MainApplication : Application(), ReactApplication {

...

  override fun onCreate() {
    super.onCreate()
    SoLoader.init(this, false)

    if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
      load() // Load native entry point for New Architecture
    }
    ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager)

    // Register SSLPinningFactory for OkHttpClientProvider
    + OkHttpClientProvider.setOkHttpClientFactory(SSLPinningFactory())
  }
}

Enter fullscreen mode Exit fullscreen mode

For Multiple Domain with .env do like this:

.env

# Domain 1
SSL_PINNING_HOSTNAME_1="-----.com"
SSL_PINNING_KEY_PRIMARY_1="---="
SSL_PINNING_KEY_BACKUP_1="---="

# Domain 2
SSL_PINNING_HOSTNAME_2="---.com"
SSL_PINNING_KEY_PRIMARY_2="---="
SSL_PINNING_KEY_BACKUP_2="---="
Enter fullscreen mode Exit fullscreen mode

SSLPinningFactory.kt

package com.yourappname

import com.facebook.react.modules.network.OkHttpClientFactory
import com.facebook.react.modules.network.OkHttpClientProvider
import okhttp3.CertificatePinner
import okhttp3.OkHttpClient

class SSLPinningFactory : OkHttpClientFactory {
    companion object {
        private val domains = mapOf(
            BuildConfig.SSL_PINNING_HOSTNAME_1 to listOf(
                "sha256/${BuildConfig.SSL_PINNING_KEY_PRIMARY_1}",
                "sha256/${BuildConfig.SSL_PINNING_KEY_BACKUP_1}"
            ),
            BuildConfig.SSL_PINNING_HOSTNAME_2 to listOf(
                "sha256/${BuildConfig.SSL_PINNING_KEY_PRIMARY_2}",
                "sha256/${BuildConfig.SSL_PINNING_KEY_BACKUP_2}"
            )
        )
    }

    override fun createNewNetworkModuleClient(): OkHttpClient {
        val certificatePinnerBuilder = CertificatePinner.Builder()

        // Iterate through domains and keys to configure SSL pinning
        for ((hostname, keys) in domains) {
            for (key in keys) {
                certificatePinnerBuilder.add(hostname, key)
            }
        }

        val certificatePinner = certificatePinnerBuilder.build()
        val clientBuilder = OkHttpClientProvider.createClientBuilder()
        return clientBuilder.certificatePinner(certificatePinner).build()
    }
}

Enter fullscreen mode Exit fullscreen mode

AppDelegate.mm

#import <TrustKit/TrustKit.h>
#import <ReactNativeConfig/ReactNativeConfig.h>


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
...

NSString *sslPinningHostname1 = [ReactNativeConfig envFor:@"SSL_PINNING_HOSTNAME_1"];
NSString *sslPinningKeyPrimary1 = [ReactNativeConfig envFor:@"SSL_PINNING_KEY_PRIMARY_1"];
NSString *sslPinningKeyBackup1 = [ReactNativeConfig envFor:@"SSL_PINNING_KEY_BACKUP_1"];

NSString *sslPinningHostname2 = [ReactNativeConfig envFor:@"SSL_PINNING_HOSTNAME_2"];
NSString *sslPinningKeyPrimary2 = [ReactNativeConfig envFor:@"SSL_PINNING_KEY_PRIMARY_2"];
NSString *sslPinningKeyBackup2 = [ReactNativeConfig envFor:@"SSL_PINNING_KEY_BACKUP_2"];

NSDictionary *trustKitConfig = @{
    kTSKSwizzleNetworkDelegates: @YES,
    kTSKPinnedDomains: @{
        sslPinningHostname1: @{  // First domain
            kTSKIncludeSubdomains: @YES,
            kTSKEnforcePinning: @YES,
            kTSKDisableDefaultReportUri: @YES,
            kTSKPublicKeyHashes: @[
                sslPinningKeyPrimary1,
                sslPinningKeyBackup1,
            ],
        },
        sslPinningHostname2: @{  // Second domain
            kTSKIncludeSubdomains: @YES,
            kTSKEnforcePinning: @YES,
            kTSKDisableDefaultReportUri: @YES,
            kTSKPublicKeyHashes: @[
                sslPinningKeyPrimary2,
                sslPinningKeyBackup2,
            ],
        },
    }
};

// Initialize TrustKit with the configuration
[TrustKit initSharedInstanceWithConfiguration:trustKitConfig];

...
...

Enter fullscreen mode Exit fullscreen mode

Conclusion

That’s it! You’ve successfully implemented SSL pinning for both iOS and Android in your React Native app. This setup ensures that only secure, trusted connections to your server are allowed, protecting your app from potential MITM attacks. 🥳

For more information, refer to the detailed guide from Callstack's SSL Pinning Blog.

Top comments (0)