SSL pinning with public key using TrustKit

In today’s advance environment, everybody will be comfortable with automation rather than applying manual efforts.

With Trustkit, one can easily validate the certificate’s public key by typing only a few lines of codes and also, it comes with extra functionalities like reporting or fallback public keys.

Step 1: Extracting the public key of the certificate

Firstly, one has to download the actual certificate used by the server.  Here, I am using the google certificate for the testing purpose.  Extract certificate with the below command:

openssl s_client -showcerts -connect www.google.co.uk:443 < /dev/null | openssl x509 -outform DER > google.cer

Note: Change the format of the downloaded certificate from .cer to .pem (which is the accepted format of TrustKit). Here, I renamed the google certificate ‘google.cer’ to ‘googleSSL.cer’ for better understanding. Use below command to change the file format:

openssl x509 -inform der -in googleSSL.cer -out certificate.pem

And finally, use the below command to extracts the public key for the pinning:

cat certificate.pem | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

Step 2: Installation

Now that one has extracted the public key, set up the TrustKit by installing TrustKit by using pods and add TrustKit in the project.

Add below line in your pod file and install the pod.

pod 'TrustKit'

Step 3: TrustKit configuration

There are two ways to configure TrustKit. The first way is by Adding keys and values to info.plist file which could be achieved by below code:

<key>TSKConfiguration</key>

                <dict>

                                <key>TSKPinnedDomains</key>

                                <dict>

                                                <key>myDomain.com</key>

                                                <dict>

                                                                <key>TSKPublicKeyHashes</key>

                                                                <array>

                                                                                <string>public key 1</string>

                                                                                <string>public key 2</string>     

                                                                </array>

                                                                <key>TSKPublicKeyAlgorithms</key>

                                                                <array>

                                                                                <string>TSKAlgorithmRsa2048</string>

                                                                </array>

                                                                <key>TSKIncludeSubdomains</key>

                                                                <true/>

                                                                <key>TSKEnforcePinning</key>

                                                                <true/>

                                                </dict>

                                </dict>

                </dict>

The second public key i.e. public key 2 (in the code above) is the backup key. So, if, there is some issue with the first key, the application will work properly with the second key (backup key).

One has to add two mandatory keys: kTSKPublicKeyAlgorithms and kTSKPublicKeyHashes to make the configuration works properly.  Here, the key kTSKPublicKeyAlgorithms specifies the encoding method of the public key and the kTSKPublicKeyHashes define the hashed version of the public key.

Note:  One has to provide at least two public keys for the pinning. The first key is from the main certificate and another key is from the backup certificate key.

The second way is by Adding the same keys and values by code in your Appdelegate file:

@UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate { 

var window: UIWindow?

static let kMyDomain = "your domain Name"

var backgroundSessionCompletionHandler: (() -> Void)? 

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 

ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)

TrustKit.setLoggerBlock { (message) in

print("TrustKit log: \(message)")} 

let trustKitConfig = [

// kTSKSwizzleNetworkDelegates: false,

kTSKPinnedDomains : [

AppDelegate.kMyDomain : [

kTSKEnforcePinning: true,

kTSKIncludeSubdomains: true,

kTSKPublicKeyAlgorithms: [kTSKAlgorithmRsa2048],

kTSKPublicKeyHashes: [

"public key 1",

"public key 2"

],

]

]

] as [String : Any] 

TrustKit.initSharedInstance(withConfiguration: trustKitConfig)

Step 4: To validate Public Key

After configuration, one can validate the public key by using PinnigValidator and URLSessionDelegate.

static func isServerCertValid(_ challenge: URLAuthenticationChallenge) -> Bool {

  guard let serverTrust = challenge.protectionSpace.serverTrust else {

      return false

  }

  let pinningValidator = TrustKit.sharedInstance().pinningValidator

  let trustDecision = pinningValidator.evaluateTrust(serverTrust, forHostname: "myWebsite.com")

  return trustDecision == .shouldAllowConnection

}

Step 5: To test the validation

The easiest way to test the public key pinning is by using Charles Proxy. The Charles Proxy works as a middleman between the application and the server and intercepts the sent requests.

By following the above steps, one can reduce the manual efforts of validating public key and automate this task with TrustKit.

Leave a Reply