Everything you need to know about SSL Pinning

SSL Pinning

Digital Certificate

  1. Subject: Provides the name of the entity (computer, user, network device, etc.) that the CA issued the certificate to.
  2. Serial Number: Provides a unique identifier for each certificate that a CA issues.
  3. Issuer: Provides a unique name for the CA that issued the certificate.
  4. Valid From: Provides the date and time when the certificate becomes valid.
  5. Valid To: Provides the date and time when the certificate is no longer considered valid.
  6. Public Key: Contains the public key of the key pair that goes with the certificate.
  7. Algorithm Identifier: Indicates the algorithm used to sign the certificate.
  8. Digital Signature: A bit string used to verify the authenticity of the certificate.
  9. Version: Version number of certificate
  10. TimeStamp: This shows the time when certificate was created

Why Do You Need SSL Certificate Pinning?

How SSL works?

  1. Client machine sends a connection request to server, server listens the request.
  2. Server gives response including public key and certificate.
  3. Client checks the certificate and sends a encrypted key to server.
  4. Server decrypt the key and sends encrypted data back to the client machine.
  5. Client receives and decrypt the encrypted data.

Types of SSL Pinning(What to Pin)?

  • Pin the certificate: You can download the server’s certificate and put this in your app bundle. At runtime, the app compares the server’s certificate to the one you’ve embedded.
  • Pin the public key: You can retrieve the certificate’s public key and include it in your code as a string. At runtime, the app compares the certificate’s public key to the one hard-coded hash string in your code.

SSL Pinning in iOS

Pining using Certificate:

Downloading The Certificate :

Pinning The Certificate

import UIKitclass ViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()guard let url = URL(string: "https://www.google.co.uk") else { return }ServiceManager().callAPI(withURL: url, isCertificatePinning: true) { (message) inlet alert = UIAlertController(title: "SSLPinning", message: message, preferredStyle: .alert)alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))self.present(alert, animated: true, completion: nil)  } }}
private var isCertificatePinning: Bool = falsefunc callAPI(withURL url: URL, isCertificatePinning: Bool, completion: @escaping (String) -> Void) {let session = URLSession(configuration: .ephemeral, delegate: self, delegateQueue: nil)self.isCertificatePinning = isCertificatePinningvar responseMessage = ""let task = session.dataTask(with: url) { (data, response, error) inif error != nil {print("error: \(error!.localizedDescription): \(error!)")responseMessage = "Pinning failed"} else if data != nil {let str = String(decoding: data!, as: UTF8.self)print("Received data:\n\(str)")if isCertificatePinning {responseMessage = "Certificate pinning is successfully completed"}else {responseMessage = "Public key pinning is successfully completed"}}DispatchQueue.main.async {completion(responseMessage)}}task.resume()}}
extension ServiceManager: URLSessionDelegate {func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {guard let serverTrust = challenge.protectionSpace.serverTrust else {completionHandler(.cancelAuthenticationChallenge, nil);return}if self.isCertificatePinning {let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)// SSL Policies for domain name checklet policy = NSMutableArray()policy.add(SecPolicyCreateSSL(true, challenge.protectionSpace.host as CFString))//evaluate server certifiactelet isServerTrusted = SecTrustEvaluateWithError(serverTrust, nil)//Local and Remote certificate Datalet remoteCertificateData:NSData =  SecCertificateCopyData(certificate!)//let LocalCertificate = Bundle.main.path(forResource: "github.com", ofType: "cer")let pathToCertificate = Bundle.main.path(forResource: "google", ofType: "cer")let localCertificateData:NSData = NSData(contentsOfFile: pathToCertificate!)!//Compare certificatesif(isServerTrusted && remoteCertificateData.isEqual(to: localCertificateData as Data)){let credential:URLCredential =  URLCredential(trust:serverTrust)print("Certificate pinning is successfully completed")completionHandler(.useCredential,credential)}else {completionHandler(.cancelAuthenticationChallenge,nil)
}
}
}
}

Pinning with Public Key (HashKey)

openssl s_client -connect the.host.name:443 | openssl x509 -pubkey -noout
openssl s_client -connect www.google.co.uk:443 | openssl x509 -pubkey -nooutBelow is the public key-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyF4KwTZBKHE+4NKftRbfy4Vo2G9igdD4GbE+wmYjouLqqACPwXLZIROvKnLrvDuvftyiwnjXFu1p1Dc8taDtEdFIDnbzGpBd2IoKHUijb+KPGKvczYpXXpISpO/0u+jOYRnPkBbCNnnxBvA+d0TSTe01UctrfN5TCxonX4E/YZMrxd9lrn5mwMGOC17ZEQIcfwPvxDl5g0LbOBFzV62O4Spt0610ui6T5rFzxuFXD5W108/GMrqtti8yo2b4ouHC2fAcM+Gfel5n136H6/Q0wJd+7oO569al1TZL8YMk37P5Uxp2TxfjXFi/61ARGarp1eEtrWTbfGhFmaZicym0wIDAQAB-----END PUBLIC KEY-----
openssl s_client -servername www.google.co.uk -connect www.google.co.uk:443 | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
Gdbmf0GLeR880mGN9WSW1XOL6v7xsVmWO6ks0LxybzU=
override func viewDidLoad() {super.viewDidLoad()guard let url = URL(string: "https://www.google.co.uk") else { return }ServiceManager().callAPI(withURL: url, isCertificatePinning: false) { (message) inlet alert = UIAlertController(title: "SSLPinning", message: message, preferredStyle: .alert)alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))self.present(alert, animated: true, completion: nil) }}
static let publicKeyHash = "Gdbmf0GLeR880mGN9WSW1XOL6v7xsVmWO6ks0LxybzU="let rsa2048Asn1Header:[UInt8] = [0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00]private func sha256(data : Data) -> String {var keyWithHeader = Data(rsa2048Asn1Header)keyWithHeader.append(data)var hash = [UInt8](repeating: 0,  count: Int(CC_SHA256_DIGEST_LENGTH))keyWithHeader.withUnsafeBytes {_ = CC_SHA256($0, CC_LONG(keyWithHeader.count), &hash)}return Data(hash).base64EncodedString()}
if let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {// Server public keylet serverPublicKey = SecCertificateCopyKey(serverCertificate)let serverPublicKeyData = SecKeyCopyExternalRepresentation(serverPublicKey!, nil )!let data:Data = serverPublicKeyData as Data// Server Hash keylet serverHashKey = sha256(data: data)// Local Hash Keylet publickKeyLocal = type(of: self).publicKeyHashif (serverHashKey == publickKeyLocal) {// Success! This is our serverprint("Public key pinning is successfully completed")completionHandler(.useCredential, URLCredential(trust:serverTrust))return}}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Anuj Rai

Anuj Rai

Senior iOS Developer #Swift #iOS #Apple #ObjectiveC