Skip to main content

Ceritifcate Pinning

This guide will teach you how to compare a certificate stored in the mobile app as being the same certificate presented by the web server that provides the HTTPS connection. In this example, you will obtain the certificate being used by ISAM HTTPS communications.

Prerequisites

Steps

  1. Obtain the certificate being used by ISAM HTTPS communications, by either:

    • Executing the following terminal command:

      input=tmpinput && touch $input && openssl s_client -connect <your_server>:<port> -showcerts 2>/dev/null <$input | openssl x509 -inform pem -outform der -out server_cert.der && rm $input
    • or, simply using the browser to navigate to the ISAM server, and then exporing the certificate to a file.
  2. Add the certifcate file exported in Step 1 (server_cert.der) to your Xcode project.

  3. Add a new Swift file to your project, and copy the following code snippet:

public class MyPinnedCertificateDelegate: NSObject, URLSessionDelegate
{
    let pinnedCertificateData: Data
    
    // Initialized with the name of the file and the file extension.
    init?(forResource: String, withExtension: String)
    {
        guard let url = Bundle.main.url(forResource: forResource, withExtension: withExtension) else
        {
            return nil
        }
        
        do
        {
            self.pinnedCertificateData = try Data(contentsOf: url)
        }
        catch
        {
            return nil
        }
    }
    
    public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
    {
        guard let serverTrust = challenge.protectionSpace.serverTrust, let presentedCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else
        {
            // Terminate further processing, no certificate at index 0
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        
        // Compare the presented certificate to the pinned certificate.
        let presentedCertificateData: NSData = SecCertificateCopyData(presentedCertificate)
        if presentedCertificateData.isEqual(to: pinnedCertificateData)
        {
            completionHandler(.useCredential, URLCredential(trust: serverTrust))
            return
        }
        
        // Don't trust the presented certificate by default.
        completionHandler(.cancelAuthenticationChallenge, nil)
    }
}


private func execute()
{
    let hostname = URL(string: "https://yourserver/mga/sps/oauth/oauth20/token")!
    let clientId = "yourClientId"
    let username = "yourUserName"
    let password = "yourPassword"
    
    guard let myPinnedCertificate = MyPinnedCertificateDelegate(forResource: "server_cert", withExtension: "der") else
    {
        print("invalid certificate.")
        return
    }
    
    OAuthContext.shared.authorize(hostname, clientId, username: username, password: password, serverTrustDelegate: myPinnedCertificate)
    {
        token, error in
        
        guard let _ = error else
        {
            print("Error: \(error.debugDescription)")
            return
        }
        
        if let token = token
        {
            // We got the token.
            print("Token: \(token)")
        }
    }
}

When the execute() function is called, myPinnedCertificate will invoke and compare the certificate passed into the MyPinnedCertificateDelegate.init (i.e. the certificate exported in Step 1) to the certificate presented by the server.

Next Steps

You have now successfully compared the certificate stored in your mobile application, to the certificate presented by the web server that provides the HTTPS connection, by using the IBMVerifyKit framework.

Play around with samples which demonstrate further functionality of the framework in the Samples section, or have a look at more quick, easy-to-implement code snippets in the Snippets section.