Do you develop native mobile applications for iOS 14+ and you want to secure your app against man-in-the-middle attacks by enabling certificate pinning?
In this article, I will explain how to leverage the simplified way of certificate pinning that Apple introduced with iOS 14. I will share details on
- how to obtain the public key
- how to pin public keys
- how to verify/test certificate pinning
It is possible to pin one or more keys of
- Certificate Authority (CA) or sub-CA certificates
- leaf certificates
As an example, I’ll pin the public key of the leaf certificate associated with SAP Mobile Services (CF, eu10 region). That’s because I am using an app developed with SAP BTP SDK for iOS which allows me to easily use SAP Mobile Services and I only have to worry to protect a single endpoint to which my app connects. The server name in my example is https://mobile-ios-sdk-tango-com-sap-btp-democai.cfapps.eu10.hana.ondemand.com. You can look up your server name on the API tab of your native/hybrid application in the Mobile Services cockpit.
How to obtain the public key
First I have to obtain the public key as the Base64-encoded SHA-256 digest of the X.509 certificate’s DER-encoded ASN.1 Subject Public Key Info structure.
This can be done by a series of openssl commands chained together. I am executing this command in the terminal on my Mac:
openssl s_client -showcerts -servername mobile-ios-sdk-tango-com-sap-btp-democai.cfapps.eu10.hana.ondemand.com -connect mobile-ios-sdk-tango-com-sap-btp-democai.cfapps.eu10.hana.ondemand.com:443 </dev/null 2>/dev/null|openssl x509 -outform PEM | openssl x509 -inform pem -noout -outform pem -pubkey | openssl pkey -pubin -inform pem -outform der | openssl dgst -sha256 -binary | openssl enc -base64
To use similar command for your example you only have to replace is the value following -servername with your specific servername:)
How to pin the public key
Let use the new property key list “NSPinnedDomains” which was introduced for the Security framework for iOS 14. It allows developers to specify a collection of certificates that App Transport Security (ATS) expects when connecting to named domains.
As I am pinning the public key of a leaf certificate I am using the Base64-encoded value (BXqb+rIKJbzS/nO3PX8F8qJsb8jeebWUplOci2Q74So=) and I am changing the Info.plist of my Xcode project:
<key>NSAppTransportSecurity</key> <dict> <key>NSPinnedDomains</key> <dict> <key>mobile-ios-sdk-tango-com-sap-btp-democai.cfapps.eu10.hana.ondemand.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSPinnedLeafIdentities</key> <array> <dict> <key>SPKI-SHA256-BASE64</key> <string>BXqb+rIKJbzS/nO3PX8F8qJsb8jeebWUplOci2Q74So=</string> </dict> </array> </dict> </dict> </dict>
How to test
My app, generated with the SAP BTP SDK Assistant for iOS, connects exclusively with SAP Mobile Services running on the Cloud Foundry environment in region eu10 (Frankfurt, Europe). I am able to verify certificate pinning by using a Proxy tool. In my case, I choose Charles Proxy. Once I enabled SSL Proxying iOS will detect this man-in-the-middle attack and will refuse the connection.
See video https://youtu.be/IK5dD921zaQ for details.
It is very simple to enable certificate pinning for apps running on iOS 14 and above.
You can read more about Identity Pinning: How to configure server certificates for your app which was published by Apple on January 14, 2021