Recently, I implemented SSL pinning in one of my React Native applications to improve the security of API communication. Since mobile apps constantly interact with backend servers, protecting those requests from attacks is extremely important.

In this article, I'll share why SSL pinning is important and how I implemented it in my React Native app.

Why SSL Pinning Is Important

None
Photo by FlyD on Unsplash

Normally, when an app communicates with a server using HTTPS, the SSL certificate is validated by the device's trusted Certificate Authority (CA).

While HTTPS already provides a good level of security, there is still a risk of Man-in-the-Middle (MITM) attacks. In such attacks, a malicious actor intercepts the communication between the mobile app and the server.

SSL pinning adds an extra layer of security by ensuring the app only trusts a specific certificate or public key, rather than any certificate issued by a trusted CA.

This means that even if someone installs a malicious certificate on the device, the app will refuse the connection.

Why I Used Public Key Pinning

Instead of pinning the entire certificate, I used public key pinning using the library:

react-native-ssl-public-key-pinning

Public key pinning is more flexible because:

• Certificates can expire or rotate • The public key often remains the same • It reduces maintenance when certificates change

This library allows us to securely make API requests while validating the server's certificate.

Step 1: Install the Library

First, install the package:

npm install react-native-ssl-public-key-pinning

or

yarn add react-native-ssl-pinning

Then install iOS dependencies:

cd ios
pod install

Step 2: Get the Public Key Hash

Next, you need the SHA256 public key hash of your server certificate.

Run this single command — it connects to your server and extracts the SHA-256 hash of the public key in one shot:

openssl s_client -connect api.yourdomain.com:443 -servername api.yourdomain.com < /dev/null 2>/dev/null \
  | openssl x509 -pubkey -noout \
  | openssl pkey -pubin -outform DER \
  | openssl dgst -sha256 -binary \
  | base64

This command returns a base64 encoded SHA256 hash of the public key.

Example output:

AbCdEfGhIjKlMnOpQrStUvWxYz1234567890abcdef=

Step 3: Configure SSL Public Key Pinning

In your React Native app, initialize the pinning configuration.

Example:

import { initializeSslPinning } from 'react-native-ssl-public-key-pinning';

initializeSslPinning({
  'yourapi.com': {
    includeSubdomains: true,
    publicKeyHashes: [
      'AbCdEfGhIjKlMnOpQrStUvWxYz1234567890abcdef=',
      'backupPublicKeyHashHere'
    ],
  },
});

It is recommended to include at least two keys:

  • Primary key
  • Backup key

This prevents the app from breaking when certificates rotate.

How to get the backup hash (critical — prevents outage)

The backup pin is your next certificate's public key or your CA's public key.

Option A — Pin the Intermediate CA (best practice):

# Get the full chain and extract intermediate cert
openssl s_client -connect api.yourdomain.com:443 -servername api.yourdomain.com -showcerts < /dev/null 2>/dev/null \
| awk '/BEGIN CERTIFICATE/{i++} i==2{print}' \
| openssl x509 -pubkey -noout \
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256 -binary \
| base64

The i==2 selects the intermediate CA cert (second in chain). Its public key almost never changes across renewals — this is the safest backup pin.

Option B — Generate your next private key now and pre-compute its hash:

openssl genrsa 2048 | openssl rsa -outform DER -pubout 2>/dev/null \
| openssl dgst -sha256 -binary | base64

Step 4: Testing the Implementation

None
Photo by Nubelson Fernandes on Unsplash

To verify the implementation, I tested the app using proxy tools such as Charles Proxy

Run the App Normally, Trigger an API call. API should work normally

Open Charles, Enable Proxy → macOS Proxy → Then start recording

Enable SSL Proxying, Go to Proxy → SSL Proxying Settings → Enable SSL Proxying → Add your domain:

Host: live.venu-iq.com
Port: 443

Install Charles Root Certificate

In Charles:

Help → SSL Proxying → Install Charles Root Certificate

Then, in Keychain Access, set:

Charles Proxy CA → Always Trust

Run your React Native app again and trigger the same API.

On iOS, the API request failed as expected.

  • This confirmed that SSL pinning was successfully blocking intercepted connections.
  • When the proxy was disabled, the API worked normally.

This ensured that the app only trusts the pinned public key.

One important rule when using public key pinning is:

Always provide at least two pins (primary + backup).

Otherwise, the app might fail if the certificate changes.

Final Thoughts

Security should always be a priority in mobile development, especially when apps communicate with sensitive backend services.

Implementing SSL public key pinning adds a strong layer of protection against network interception attacks.

If your React Native app communicates with production APIs, I highly recommend considering SSL pinning as part of your security strategy.

Please clap if you liked the post.