Server Authentication Broken in Swift 2.0 and Xcode 7

I just upgraded my code to Swift 2.0 to work with Xcode 7. My application authenticates with NSURLAuthenticationMethodServerTrust and NSURLAuthenticationMethodClientCertificate .

Problem: NSURLAuthenticationMethodServerTrust authentication stopped working on my simulator, but still works on my test device with iOS 8.3. Also, my old project, which is not Swift 2.0, also works.

Error: NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)

Error received from NSURLSession:

 Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo=0x7fcf75053070 {NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x7fcf73700d00>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorCodeKey=-9802, NSUnderlyingError=0x7fcf735284b0 "The operation couldn't be completed. (kCFErrorDomainCFNetwork error -1200.)", NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://mywebapi/dosomething, NSErrorFailingURLStringKey=https://mywebapi/dosomething, _kCFStreamErrorDomainKey=3} [GetOneTimeTokenController.swift:76] 

I am still aiming to deploy iOS 8.0.

This is how I handle the authentication call (using a self-signed certificate):

 if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate { let urlCredential:NSURLCredential = NSURLCredential( identity: identityAndTrust.identityRef, certificates: identityAndTrust.certArray as [AnyObject], persistence: NSURLCredentialPersistence.ForSession); completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, urlCredential); } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(trust: challenge.protectionSpace.serverTrust!)); } else { challenge.sender?.continueWithoutCredentialForAuthenticationChallenge(challenge) Logger.sharedInstance.logMessage("Unexpected Authentication Challange", .Error); } 
+5
source share
3 answers

You do not have a traversal of the TLS layer. Please find the answer below that worked for me using a self-signed certificate.

Below are code changes that work great with a self-signed SSL certificate

  func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { if challenge.protectionSpace.authenticationMethod == (NSURLAuthenticationMethodServerTrust) { let serverTrust:SecTrustRef = challenge.protectionSpace.serverTrust! let certificate: SecCertificateRef = SecTrustGetCertificateAtIndex(serverTrust, 0)! let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate))! let cerPath: String = NSBundle.mainBundle().pathForResource("xyz.com", ofType: "cer")! let localCertificateData = NSData(contentsOfFile:cerPath)! if (remoteCertificateData.isEqualToData(localCertificateData) == true) { let credential:NSURLCredential = NSURLCredential(forTrust: serverTrust) challenge.sender?.useCredential(credential, forAuthenticationChallenge: challenge) completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)) } else { completionHandler(NSURLSessionAuthChallengeDisposition.CancelAuthenticationChallenge, nil) } } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate { let path: String = NSBundle.mainBundle().pathForResource("client", ofType: "p12")! let PKCS12Data = NSData(contentsOfFile:path)! let identityAndTrust:IdentityAndTrust = self.extractIdentity(PKCS12Data); let urlCredential:NSURLCredential = NSURLCredential( identity: identityAndTrust.identityRef, certificates: identityAndTrust.certArray as? [AnyObject], persistence: NSURLCredentialPersistence.ForSession); completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, urlCredential); } else { completionHandler(NSURLSessionAuthChallengeDisposition.CancelAuthenticationChallenge, nil); } } struct IdentityAndTrust { var identityRef:SecIdentityRef var trust:SecTrustRef var certArray:AnyObject } func extractIdentity(certData:NSData) -> IdentityAndTrust { var identityAndTrust:IdentityAndTrust! var securityError:OSStatus = errSecSuccess let path: String = NSBundle.mainBundle().pathForResource("client", ofType: "p12")! let PKCS12Data = NSData(contentsOfFile:path)! let key : NSString = kSecImportExportPassphrase as NSString let options : NSDictionary = [key : "xyz"] //create variable for holding security information //var privateKeyRef: SecKeyRef? = nil var items : CFArray? securityError = SecPKCS12Import(PKCS12Data, options, &items) if securityError == errSecSuccess { let certItems:CFArray = items as CFArray!; let certItemsArray:Array = certItems as Array let dict:AnyObject? = certItemsArray.first; if let certEntry:Dictionary = dict as? Dictionary<String, AnyObject> { // grab the identity let identityPointer:AnyObject? = certEntry["identity"]; let secIdentityRef:SecIdentityRef = identityPointer as! SecIdentityRef!; print("\(identityPointer) :::: \(secIdentityRef)") // grab the trust let trustPointer:AnyObject? = certEntry["trust"]; let trustRef:SecTrustRef = trustPointer as! SecTrustRef; print("\(trustPointer) :::: \(trustRef)") // grab the cert let chainPointer:AnyObject? = certEntry["chain"]; identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef, trust: trustRef, certArray: chainPointer!); } } return identityAndTrust; } 

Changes made to info.plist

  <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>NSExceptionDomains</key> <dict> <key>amazonaws.com.cn</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> <key>NSThirdPartyExceptionMinimumTLSVersion</key> <string>TLSv1.0</string> </dict> <key>amazonaws.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> <key>NSThirdPartyExceptionMinimumTLSVersion</key> <string>TLSv1.0</string> </dict> <key>xyz.com</key> <dict> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSTemporaryExceptionMinimumTLSVersion</key> <string>TLSv1.2</string> <key>NSRequiresCertificateTransparency</key> <false/> <key>NSIncludesSubdomains</key> <true/> </dict> </dict> <key>NSAllowsArbitraryLoads</key> <false/> </dict> </plist> 
+2
source

Your simulator most likely runs iOS 9. iOS 9 uses TLS 1.2. If you do not use it, your requests will fail.

For more information contact this post .

You can get around it by putting it in your Info.plist :

 <key>NSAppTransportSecurity</key> <dict> <!--Include to allow all connections (DANGER)--> <key>NSAllowsArbitraryLoads</key> <true/> </dict> 

but this is just a temporary workaround until you can implement TLS 1.2.

+17
source

I had the same issue with iOS9 and xcode 7 Just add:

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

to the plist file. It works, but this is a temporary solution until you can implement TLS 1.2.

0
source

All Articles