9
votes

I have developed an IOS App that is communicating with a Server using HTTPs (I'm also the developer of the server, it's embedding a Tomcat server).

This App is working without any issues with IOS8 but it's not with IOS9.

I get the following error when sending an HTTPs request:

Session download has failed : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo=0x7fa9c44b2e40 {NSErrorFailingURLStringKey=https://127.0.0.1:8443/MyServer/MyApp, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorFailingURLKey=https://127.0.0.1:8443/MyServer/MyApp, _kCFStreamErrorCodeKey=-9802, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made.}

Based on Apple documentation when the error Code=-1200 happens it's because TLSv1.1 instead of TLSv1.2. I have verified with an openSSL command and my server is using TLSv1.2.

When setting in the info.plist the property NSAllowsArbitraryLoads = true, the error disappear but I don't want to keep it like this.

My server is using a self signed certificate, maybe it's the cause of the issue? Below is the code I'm using for didReceiveChallenge

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {

[RequestURLUtilities dumpChallenge:challenge];


if (challenge.protectionSpace != Nil) {
    NSURLProtectionSpace *protection = challenge.protectionSpace;

    SecTrustRef sslState = protection.serverTrust;
    if (sslState == Nil) {
        NSLog(@"%s Warning: empty serverTrust",__PRETTY_FUNCTION__);
    }


    if ([protection.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

        NSLog(@"%s => NSURLAuthenticationMethodServerTrust", __PRETTY_FUNCTION__);
        // warning normaly I should check the validity of the SecTrustRef before to trust
        NSURLCredential* credential = [NSURLCredential credentialForTrust:sslState];

        // Put in command for test

       completionHandler(NSURLSessionAuthChallengeUseCredential, credential);

    } else {
        NSLog(@"%s => Called for another challenge", __PRETTY_FUNCTION__);
       completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, NULL);
    }

}

Should I create my own CA and then my own certificates? How to proceed? What is missing? Any help will be greatly appreciated.

Sebastien.

2

2 Answers

11
votes

After some discussion with Apple Support, the issue is due to the self signed certificate.

ATS trusts only certificate signed by a well known CA, all others are rejected. As a consequence the only solution with a Self signed certificate is to set an exception with NSExceptionDomains.

5
votes

Unfortunately you will need to either get a cert from a major CA or set an exception which is only a temp fix. You really should fix your SSL and test it over at SSL Labs

Right click your Info.plist and select open as source code. Following code will allow you to bypass for your server only. Documentation is here

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>yourserver.com</key>
    <dict>
      <!--Include to allow subdomains-->
      <key>NSIncludesSubdomains</key>
      <true/>
      <!--Include to allow HTTP requests-->
      <key>NSExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      <!--Include to specify minimum TLS version-->
      <key>NSExceptionMinimumTLSVersion</key>
      <string>TLSv1.1</string>
      <key>NSExceptionRequiresForwardSecrecy</key>
      <false/>
    </dict>
  </dict>
</dict>

You can also set this by editing the info.plist as a property list. See this article WORKING WITH APPLEā€™S APP TRANSPORT SECURITY