15

I have the following code (swift implementation):

func connection(connection: NSURLConnection, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace) -> Bool
{
    return protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
}

func connection(connection: NSURLConnection, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge)
{
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
    {

        if challenge.protectionSpace.host == "myDomain"
        {
            let credentials = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust)
            challenge.sender.useCredential(credentials, forAuthenticationChallenge: challenge)
        }
    }

    challenge.sender.continueWithoutCredentialForAuthenticationChallenge(challenge)

}

It works perfectly in iOS 8.x, but does not work iOS 7.x In iOS 7.x I have error:

NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)

Any idea? thank you!!!

Carlos Cardoso
  • 315
  • 1
  • 2
  • 9

3 Answers3

25

Both connection:canAuthenticateAgainstProtectionSpace: and connection:didReceiveAuthenticationChallenge: are deprecated in iOS 8 anyway so you should use other methods.

What I am using in my projects is a delegate method of NSURLSessionDelegate. Adhere to that protocol then add this method:

func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void) {
    completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust))
}

Then, when you use initialize NSURLSession with delegate set to self. For example:

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

Then use that session instance to call dataTaskWithRequest method on:

var task = session.dataTaskWithRequest(request){
    (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
    if error != nil {
        callback("", error.localizedDescription)
    } else {
        var result = NSString(data: data, encoding:
            NSASCIIStringEncoding)!
    }
}
task.resume()

Complete working example can be found here.

For security reasons, if you use a self-signed certificate I recommend also implementing public key pinning (https://gist.github.com/edwardmp/df8517aa9f1752e73353)

edwardmp
  • 6,339
  • 5
  • 50
  • 77
  • Is this at all secure without public key pinning? As far as i can see, you don't check the certificate matches what it should – Hamzah Malik Sep 14 '15 at 21:15
  • How can you check the certificate matches when you don't use public key pinning? Key pinning is not requested, but I recommend it thats why I included the link at the bottom to a snippet.. – edwardmp Sep 29 '15 at 14:46
  • The traffic is still encrypted without using public key pinning, but I guess a Man In The Middle attack (MITM) makes it possible to offer another certificate in order to intercept the traffic. – edwardmp Sep 30 '15 at 22:10
  • Does not work for me - I get some "Error Domain=NSURLErrorDomain Code=-999". But it works on with valid certificates. – coyer Nov 24 '16 at 16:40
  • 1
    @coyer -999 error means that the request is cancelled. Maybe you or some other library you use cancels the request (see http://stackoverflow.com/questions/25390073/what-is-nsurlerrorcancelled-999-in-ios)? Or maybe something is wrong with the server itself (e.g. the certificate is self signed but the hostname is also incorrect) – edwardmp Nov 24 '16 at 16:43
  • Could you please check this: https://stackoverflow.com/q/56627757/1364053 – nr5 Jun 17 '19 at 08:48
0

inherit class with URLSessionDelegate

create a session object

let config = URLSessionConfiguration.default


let session = Foundation.URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)


 let task = session.dataTask(with: httpRequest as URLRequest, completionHandler: {requestData, response, errorData -> Void in

            if errorData == nil {

                dataCallback(requestData! as NSData)
            }
            else {

                let error = NSError(domain: "Err-1001", code: 11, userInfo:nil)
                failureCallback(error)
            }
        });

        task.resume() 

Add delegate methad

func urlSession(_ session: URLSession, task: URLSessionTask, didReceive     challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
     completionHandler(
        .useCredential,
        URLCredential(trust: challenge.protectionSpace.serverTrust!))
 }

add this in your info.plist file

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>xyc.com</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <key>NSExceptionRequiresForwardSecrecy</key>
            <true/>
            <key>NSExceptionMinimumTLSVersion</key>
            <string>TLSv1.2</string>
            <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
            <false/>
            <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
            <true/>
            <key>NSThirdPartyExceptionMinimumTLSVersion</key>
            <string>TLSv1.2</string>
            <key>NSRequiresCertificateTransparency</key>
            <false/>
        </dict>
    </dict>
</dict>
Chris Prince
  • 7,288
  • 2
  • 48
  • 66
-1

Swift 5 Solution

Note These settings are for testing purpose only, don't push changes in production app

Steps

  1. Add "Allow Arbitrary Loads" key to "YES" under "App Transport Security Settings" in info.plist

  2. Create new session object with delegate to self

    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)

  3. Implement URLSessionDelegate protocol to current class

    extension YouClass: URLSessionDelegate{ func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!)) } }

Modified version of: https://stackoverflow.com/a/30820452/3754976

Afzaal Ahmad
  • 816
  • 10
  • 16