I am using the URLSession library to connect to certain pages using the https protocol. To get access to certain urls I had to create a delegate to include the public part of the certificate offered by the server. The problem I find is that in some urls the application does not call the method that I created in the delegate. What could be the problem? I include the snippet of the URLSession class and the delegated class that I created. let delegate: URLSessionDelegate = SessionDelegate() as URLSessionDelegate let urlSession = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil) let task = urlSession.dataTask(with: request as URLRequest){ data, response, error in if error == nil { var htmlCode: String = "" if charset == Constantes.CHARSET_UTF8 { htmlCode = String(data: data!, encoding: .utf8)! } else { htmlCode = String(data: data!, encoding: .ascii)! } callback(htmlCode, nil) return } else { callback("", error.debugDescription) } } task.resume() And my delegate class: class SessionDelegate:NSObject, URLSessionDelegate { let certificadosName: [String] = ["AC_Administracion_Publica","ac_raiz_fnmt","Camerfirma_AAPP_II_Chambers_of_Commerce_Root","Camerfirma_Corporate_Server_II_Chambers_of_Commerce_Root","Chambers_of_Commerce_Root","claveRaiz","DigiCert_High_Assurance_EV_Root_CA","Entrust_Root_Certification_Authority_G","GeoTrust_SSL_CA_G_GeoTrust_Global_CA","Izenpe_com"] let certFileType = "cer" func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard challenge.previousFailureCount == 0 else { challenge.sender?.cancel(challenge) // Inform the user that the user name and password are incorrect completionHandler(.cancelAuthenticationChallenge, nil) return } if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust && challenge.protectionSpace.serverTrust != nil { let trust = challenge.protectionSpace.serverTrust var certs: [SecCertificate] = [SecCertificate]() for certificadoName in certificadosName { let pem = Bundle.main.url(forResource: certificadoName, withExtension: certFileType) let data = NSData(contentsOf: pem!) let cert = SecCertificateCreateWithData(nil, data!) certs.append(cert!) } SecTrustSetAnchorCertificates(trust!, certs as CFArray) var result=SecTrustResultType.invalid if SecTrustEvaluate(trust!,&result)==errSecSuccess { if result==SecTrustResultType.proceed || result==SecTrustResultType.unspecified { let proposedCredential = URLCredential(trust: trust!) completionHandler(.useCredential,proposedCredential) return } } } completionHandler(.performDefaultHandling, nil) } }
Thanks!
That server does not meet ATS’s forward secrecy requirement:
% TLSTool s_client -connect www.boe.es:443
* input stream did open
* output stream did open
* output stream has space
* protocol: TLS 1.2
* cipher: RSA_WITH_AES_256_GCM_SHA384
* trust result: unspecified
* certificate info:
* 0 + rsaEncryption 2048 sha256-with-rsa-signature 'www.boe.es'
* 1 + rsaEncryption 2048 sha256-with-rsa-signature 'TERENA SSL High Assurance CA 3'
* 2 + rsaEncryption 2048 sha1-with-rsa-signature 'DigiCert High Assurance EV Root CA'
^C
Note the cypher suite is
RSA_WITH_AES_256_GCM_SHA384
, whereas ATS requires forward secrecy via an ECDHE cypher suite. See the discussion of
NSExceptionRequiresForwardSecrecy
in the
NSExceptionDomains
documentation, and the background info in
Preventing Insecure Network Connections.
Consider this code snippet:
NSLog("task will start")
let url = URL(string: "https://www.boe.es")!
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 60.0)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error as NSError? {
NSLog("task transport error %@ / %d", error.domain, error.code)
return
}
let response = response as! HTTPURLResponse
let data = data!
NSLog("task finished with status %d, bytes %d", response.statusCode, data.count)
}.resume()
By default, it fails with a -1200 error. I can get it working by adding the following ATS configuration:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>www.boe.es</key>
<dict>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
</dict>
</dict>
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"