4

In order to test my app I set up a server Apache 2.4.12 with TLS 1.2 and a self signed certificate. The problem is that when I run my app and I try to communicate with my local server I get this error:

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
nil
Optional(Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred  and a secure connection to the server cannot be made." UserInfo= {NSLocalizedDescription=An SSL error has occurred and a secure connection to the  server cannot be made., NSLocalizedRecoverySuggestion=Would you like to connect  to the server anyway?...

and this is because a self signed certificate in not reliable of corse. I had read somewhere here in stackoverflow that, in the class where I use the NSURLSession(), I had to add the following delegate:

func URLSession(session: NSURLSession,
    task: NSURLSessionTask,
    didReceiveChallenge challenge: NSURLAuthenticationChallenge,
    completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?)
    -> Void) {

    completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
}

and then perform:

func requestLoginWithURL (requestURL: NSURL, completionHandler: (success: Bool?) -> Void) {
    let configuration =
    NSURLSessionConfiguration.defaultSessionConfiguration()

    let urlRequest: NSURLRequest = NSURLRequest(URL: requestURL)

    let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())

    /*
    dataTaskWithRequest: creates an HTTP request for the specified URL request object, and calls a handler upon completion.
    */
    let task = session.dataTaskWithRequest( ...)
}

but it seems that this doesn't work anyway. So I was wondering if there was a way to force the self assigned certificate to be accepted without adding that xml code to info.plist to disable any form secure communication. Here's httpd-ssl.conf in my server:

<VirtualHost *:443>
      DocumentRoot "/Applications/AMPPS/www"
      ServerName localhost
      SSLEngine on
      SSLProtocol all -SSLv2 -SSLv3
      SSLHonorCipherOrder on
      SSLCertificateFile "/Applications/AMPPS/apache/conf/server.crt"
      SSLCertificateKeyFile "/Applications/AMPPS/apache/conf/server.key"
</VirtualHost>

Is there any way to make this work? Just to show my teacher that I am building my app accordant to iOS 9 ATS. Thanks to all.

SOLUTION:

Add this to file info.plist which will force ATS to accept self signed certificate:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>localhost</key>
        <dict>
            <!--Include to allow subdomains-->
            <key>NSIncludesSubdomains</key>
            <true/>
            <!--Include to allow HTTP requests-->
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <!--
            <key>NSExceptionMinimumTLSVersion</key>
            <string>TLSv1.1</string>
            <key>NSExceptionRequiresForwardSecrecy</key>
            <false/> -->
        </dict>
    </dict>
</dict>

However, as explain here this is not enough. In each class where you will use NSURLSession you have to declare the following delegate:

class LoginService: NSObject, NSURLSessionDelegate {

func URLSession(session: NSURLSession,
    task: NSURLSessionTask,
    didReceiveChallenge challenge: NSURLAuthenticationChallenge,
    completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?)
    -> Void) {

    completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
}

...

}

And then perform something like the following:

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()

    let urlRequest: NSURLRequest = NSURLRequest(URL: requestURL)

    let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())

    /*
    dataTaskWithRequest: creates an HTTP request for the specified URL request object, and calls a handler upon completion.
    */
    let task = session.dataTaskWithRequest(urlRequest...)

This works for me. Remember that you must use Apache 2.4.x because it's the only version supporting TLS 1.2. Hope this will help.

SagittariusA
  • 5,289
  • 15
  • 73
  • 127
  • If you are using a self-signed cert you will either need to add the signing ca details and trust it or disable ATS – Paulw11 Sep 24 '15 at 21:00
  • But I'm reading some answers talking about primitives like setAllowsAnyHTTPSCertificate but they're written in objective-c and I'm not able to use them... – SagittariusA Sep 24 '15 at 21:02
  • All of that stuff is pre-ATS. You can disable certificate checking in the higher-level frameworks (and still need to do so if they perform certificate checks) but ATS is baked in to ios9 and it can't be overridden at runtime - you need to disable it it info.plist if you don't want it. – Paulw11 Sep 24 '15 at 21:04
  • @Paulw11 I don't want to disable ATS. I want to add self signed certification and make an HTTPS connection using NSURLSession with the server(which is made in node.js) in Xcode7 and is. Can you have any example or document for this? – Amit Raj Sep 30 '15 at 13:20
  • You need to install the public key of the CA that was used to sign the server's certificate into the iOS device and trust it. If you are running in iOS9 then untrusted certificates won't work if ATS is enabled – Paulw11 Sep 30 '15 at 13:22
  • @AmitRaj: this could help you I guess: http://stackoverflow.com/questions/32716766/swift-xcode-7-nsurlsession-nsurlconnection-http-load-failed-kcfstreamerrordoma – SagittariusA Sep 30 '15 at 13:24
  • Basically that shows how to disable ATS for `localhost` – Paulw11 Sep 30 '15 at 13:26
  • I don't think ATS is really disabled...not in strict meaning – SagittariusA Sep 30 '15 at 13:30
  • for example if I try to delete only the delegate method URLSession() ATS annoys me with error 9802...so it means it's active somehow – SagittariusA Sep 30 '15 at 13:34
  • @Paulw11 I dragged and dropped the .crt into my iOS simulator and installed it, but I'm still getting an error in the server `ERROR OpenSSL::SSL::SSLError: SSL_accept returned=1 errno=0 state=error: no shared cipher` and in the iOS app `NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)`. How do I solve this? does the .crt and .key need to be in a specific folder? – Rodrigo Ruiz Oct 05 '15 at 06:20

0 Answers0