1

I am attempting to call a WebAPI with a self signed ceritficate. I have disabled ATS as far as I can by acting the following to my info.plist:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

However, even after adding this code I still receive the same error. The code and error are below. Any assistance would be appreciated.

 let postEndpoint: String = "https://api.rimorton.com/com.absolute.am.webapi/api/login"
    let url = NSURL(string: postEndpoint)
    let urlRequest = NSURLRequest(URL: url!)
    let config = NSURLSessionConfiguration.defaultSessionConfiguration()
    let session = NSURLSession(configuration: config)

    let task = session.dataTaskWithRequest(urlRequest, completionHandler: {
        (data, response, error) in

        guard error == nil else {
            print("error calling GET on /posts/1")
            print(error)
            return
        }

        guard let responseData = data else {
            print("Error: did not receive data")
            return
        }

        // parse the result as JSON, since that's what the API provides
        let post: NSDictionary
        do {
            post = try NSJSONSerialization.JSONObjectWithData(responseData,
                options: []) as! NSDictionary
        } catch  {
            print("error trying to convert data to JSON")
            return
        }
        // now we have the post, let's just print it to prove we can access it
        print("The post is: " + post.description)

        // the post object is a dictionary
        // so we just access the title using the "title" key
        // so check for a title and print it if we have one
        if let postTitle = post["title"] as? String {
            print("The title is: " + postTitle)
        }
    })
    task.resume()

Error: 2016-02-28 19:40:03.521 LANrevTarget_Dev[19173:878737] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813) error calling GET on /posts/1 Optional(Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “api.rimorton.com” which could put your confidential information at risk." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9813, NSErrorPeerCertificateChainKey={type = immutable, count = 1, values = ( 0 : )}, NSUnderlyingError=0x7fb78b542e00 {Error Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=, _kCFNetworkCFStreamSSLErrorOriginalValue=-9813, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9813, kCFStreamPropertySSLPeerCertificates={type = immutable, count = 1, values = ( 0 : )}}}, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “api.rimorton.com” which could put your confidential information at risk., NSErrorFailingURLKey=https://api.rimorton.com/com.absolute.am.webapi/api/login, NSErrorFailingURLStringKey=https://api.rimorton.com/com.absolute.am.webapi/api/login, NSErrorClientCertificateStateKey=0})

George M Ceaser Jr
  • 1,497
  • 3
  • 23
  • 52

1 Answers1

0

Allowing arbitrary loads doesn't disable certificate validation. It just allows non-encrypted HTTP requests, plus requests where the cert is of a type that isn't considered secure enough (lacking forward secrecy, for example).

To allow a specific self-signed key, read this:

https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html

The examples are all Objective-C, but they should give you the basic idea. Specifically, you'll want that addAnchorToTrust function.

Then, take a look at the connection:willSendRequestForAuthenticationChallenge:challenge on that page, and do basically the exact same thing in your URLSession:didReceiveChallenge:completionHandler: method, except that everywhere it does:

[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];

you instead call:

completionHandler(NSURLSessionAuthChallengeUseCredential, credential);

and wherever it calls

[connection cancel]

you instead call

completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);

Hope that helps. Also, you'll need to store a copy of the trusted cert in your app bundle and read it into your app as described here:

How can I get SecKeyRef from DER/PEM file

Community
  • 1
  • 1
dgatwood
  • 10,129
  • 1
  • 28
  • 49