How to fix iOS 9 ATS SSL error when your server supports TLS version 1.2
After installing Xcode 7 and trying to run Mint Bills under iOS 9 for the first time I was getting the following infamous error:
Connection failed! Error — -1200 An SSL error has occurred and a secure connection to the server cannot be made.
With iOS 9 apple has introduced App Transport Security.
Here’s what Apple has to say about ATS:
App Transport Security (ATS) enforces best practices in the secure connections between an app and its back end. ATS prevents accidental disclosure, provides secure default behavior, and is easy to adopt; it is also on by default in iOS 9 and OS X v10.11. You should adopt ATS as soon as possible, regardless of whether you’re creating a new app or updating an existing one.
If you’re developing a new app, you should use HTTPS exclusively. If you have an existing app, you should use HTTPS as much as you can right now, and create a plan for migrating the rest of your app as soon as possible. In addition, your communication through higher-level APIs needs to be encrypted using TLS version 1.2 with forward secrecy. If you try to make a connection that doesn’t follow this requirement, an error is thrown. If your app needs to make a request to an insecure domain, you have to specify this domain in your app’sInfo.plist file.
The thing is — our server DOES support TLS version 1.2 and WE ARE using NSURLSession. What could be the problem then? Apple has released the full requirements list for supporting ATS and it turned out we were working with TLS version 1.2 but were still missing some of the other requirements.
Here’s the full check list:
- TLS requires at least version 1.2.
- Connection ciphers are limited to those that provide forward secrecy (see below for the list of ciphers.)
- The service requires a certificate using at least a SHA256 fingerprint with either a 2048 bit or greater RSA key, or a 256bit or greater Elliptic-Curve (ECC) key.
- Invalid certificates result in a hard failure and no connection.
Full list of accepted ciphers:
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
What do you do when your server does not support TLS version 1.2 and you want to bypass ATS all together? Apple allows developers to do that by adding the following key to the info.plist:
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
If your application connects to more than one server and only some of them supports TLS version 1.2 — you can also add specific exceptions by adding this to the info.plist:
<key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>testdomain.com</key> <dict> <key>NSIncludesSubdomains</key> <false/> <key>NSExceptionAllowInsecureHTTPSLoads</key> <false/> <key>NSExceptionRequiresForwardSecrecy</key> <true/> <key>NSExceptionMinimumTLSVersion</key> <string>TLSv1.2</string> <key>NSThirdPartyExceptionAllowInsecureHTTPSLoads</key> <false/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <true/> <key>NSThirdPartyExceptionMinimumTLSVersion</key> <string>TLSv1.2</string> <key>NSRequiresCertificateTransparency</key> <false/> </dict> … </dict> </dict>
This was originally asked and answered by me on StackOverflow
Additional information provided by Nilesh Patel