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.