How to accept a self-signed SSL certificate using iOS 7 NSURLSession and its family of delegate methods for development purposes?

I am developing an application for the iPhone. During development, I need to connect to the server using a self-signed SSL certificate. I'm pretty sure that - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler is my opportunity to write an exception code to allow this. However, I cannot find any resources that will tell me how to do this. I see the following error in the log:

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

In addition to this, when I am NSLog(@"error = %@", error); from the above delegate method I get:

Domain Error = NSURLErrorDomain Code = -1202 "The certificate for this server is not valid. Perhaps you are connecting to a server that is pretending to be" api.mydevelopmenturl.com ", which may confidential information is at risk." UserInfo = 0x10cbdbcf0 {NSUnderlyingError = 0x112ec9730 "The certificate for this server is invalid. Perhaps you are connecting to a server that is pretending to be" api.mydevelopmenturl.com ", which may provide your sensitive information under threat.", NSErrorFailingURLStringKey = https: mydevelopmenturl.com/posts , NSErrorFailingURLKey = https://api.mydevelopmenturl.com/posts , NSLocalizedRecoverySuggestion = Do you want to connect to the server anyway ?, NSURLErrorFailingURLPeerTrustErrorKey =, NSLocalizedDescription = This certificate is not valid. Perhaps you are connecting to a server that pretends to be "api.mydevelopmenturl.com", which may information is at risk.}

Any ideas on how to solve this problem? Please send the code as I read the concept documents and I do not understand them. Here is an example of what is outside of me: https://developer.apple.com/library/content/technotes/tn2232/_index.html

+47
ios objective-c iphone ssl ios7
Oct 22 '13 at 0:45
source share
11 answers

This works for me:

 NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:Nil]; ... ... - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{ if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){ if([challenge.protectionSpace.host isEqualToString:@"mydomain.com"]){ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential,credential); } } } 
+92
Dec 01 '13 at 16:07
source share

Apple has Technical Note 2232 , which is quite informative and describes in detail the assessment of the trust of an HTTPS server .

In this case, error -1202 in the NSURLErrorDomain domain is NSURLErrorServerCertificateUntrusted , which means that the server trust check failed. You can also get many other errors; Appendix A: General Server Trust Validation Errors are listed the most common.

From the Technical Note:

In most cases, the best way to allow server trust assessment to fail is to fix the server. This has two advantages: it offers better security, and it reduces the amount of code you have to write. the rest of this technote describes how you can diagnose server trust with evaluation errors and, if it is not possible to install a server, how you can configure server trust verification to continue without connecting your user completely.

The specific bit that is related to this question is the NSURLSession server trust assessment section:

NSURLSession allows you to configure HTTPS server trust verification to implement the -URLSession:didReceiveChallenge:completionHandler: delegate. To configure an HTTPS server trust assessment, find a task whose security space has the NSURLAuthenticationMethodServerTrust authentication NSURLAuthenticationMethodServerTrust . To solve these problems as described below. For other problems that you don’t care, call the completion handler block using the NSURLSessionAuthChallengePerformDefaultHandling and NULL credentials.

When working with the NSURLAuthenticationMethodServerTrust authentication call, you can get the trust object from the security call by calling the -serverTrust method. After using the trust object to execute the HTTPS server’s own user trust, you must solve the problem in one of two ways:

If you want to reject the connection, call the termination handler block using the NSURLSessionAuthChallengeCancelAuthenticationChallenge location and NULL credentials.

If you want to allow the connection, create credentials from your trust object (using +[NSURLCredential credentialForTrust:] ) and call the completion handler block with the specified credentials and NSURLSessionAuthChallengeUseCredential .

The bottom line of all this is that if you implement the following delegate method, you can override the server trust for a specific server:

 - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler { if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { if([challenge.protectionSpace.host isEqualToString:@"domaintoverride.com"]) { NSURLCredential *credential = [NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential,credential); } else completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); } } 

Note that you must handle both the host case corresponding to the one you want to override and all other cases. If you do not handle the "all other cases" part, the result of the behavior is undefined.

+21
Jun 01 '14 at 20:30
source share

Find a trusted SSL Certificate Authority on the Internet offering a 90-day free trial for new certificates. Install the certificate on your server. Now you have 90 days to develop your application until you can decide whether to pay money to "renew" the certificate. This is the best answer for me, since my decision to use a self-signed certificate was financially motivated, and 90 days give me enough time to develop my application to the extent that I can decide whether to spend money on an SSL certificate or not. This approach allows Avoid the need to deal with security implications for working with a code base that adjusts to accept self-signed certificates. Sweet! Yay for boot!

+11
Oct 24 '13 at 1:17
source share

Do yourself a great service and do not do it.

Start by reading the article The Most Dangerous Code in the World: Validating SSL Certificates in Non-Browser Software , especially in Section 10, Breaking or Disabling Certificate Validation. He specifically invokes a Cocoa blog that specifically describes how to do what you ask.

But do not. Disabling SSL certificate verification is an introduction to your application of a time bomb. Someday, someday, it will be accidentally left turned on, and the assembly will fall into the wild. And on this day, your users will be at serious risk.

Instead, you should use a certificate signed with an intermediate certificate that you can install and trust on this particular device, which will allow you to successfully perform SSL verification without jeopardizing any device other than your own (and only then, temporarily).

+8
Oct 22 '13 at 3:31 on
source share

Same as friherd solution, but fast:

 func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust{ let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!) completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential,credential); } } 
+5
Sep 24 '15 at 20:46
source share

just need to add .cer to SecTrust and pass ATS

 class NSURLSessionPinningDelegate: NSObject, URLSessionDelegate { func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) { if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) { if let trust = challenge.protectionSpace.serverTrust, let pem = Bundle.main.path(forResource: "https", ofType: "cer"), let data = NSData(contentsOfFile: pem), let cert = SecCertificateCreateWithData(nil, data) { let certs = [cert] SecTrustSetAnchorCertificates(trust, certs as CFArray) completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: trust)) return } } // Pinning failed completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil) } } 
+1
Jan 23 '17 at 10:22
source share

For Swift 3.0 / 4

If you just want to allow any self-signed certificates, you can use the following approach to implement URLSessionDelegate. Apple provides additional information on how to use URLSessionDelegate for all kinds of authentication methods: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/AuthenticationChallenges.html

First implement the delegate method and assign the appropriate delegate:

 let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil) let task = urlSession.dataTask(with: urlRequest).resume() 

Now we implement the delegate method https://developer.apple.com/documentation/foundation/nsurlsessiondelegate/1409308-urlsession?language=objc

 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 } // Within your authentication handler delegate method, you should check to see if the challenge protection space has an authentication type of NSURLAuthenticationMethodServerTrust if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust // and if so, obtain the serverTrust information from that protection space. && challenge.protectionSpace.serverTrust != nil && challenge.protectionSpace.host == "yourdomain.com" { let proposedCredential = URLCredential(trust: challenge.protectionSpace.serverTrust!) completionHandler(URLSession.AuthChallengeDisposition.useCredential, proposedCredential) } } 

However, you can adapt the adoption of any self-signed certificate for your provided domain to be very specific. Be sure to add this certificate earlier to install it in the kit. I called it here "cert.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 && challenge.protectionSpace.host == "yourdomain.com" { if let trust = challenge.protectionSpace.serverTrust, let pem = Bundle.main.url(forResource:"cert", withExtension: "cer"), let data = NSData(contentsOf: pem), let cert = SecCertificateCreateWithData(nil, data) { let certs = [cert] SecTrustSetAnchorCertificates(trust, certs as CFArray) let proposedCredential = URLCredential(trust: trust) completionHandler(URLSession.AuthChallengeDisposition.useCredential, proposedCredential) return } } } 
+1
Sep 13 '17 at 11:29 on
source share

This works great for me to go through on my own:

 Delegate : NSURLSessionDelegate - (void)URLSession:(NSURLSession *)session **task**:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); } 
0
Nov 17 '15 at 5:55
source share

Perhaps the best way is to give the user the ability to accept a certificate confirming (visually) that the URL is accurate to access the service. For example, if a host is entered into the configuration of an application, test it in a user record and allow the user right there.

Keep in mind that this “user confirmation” tactic is used by Safari, so it’s really justified by Apple, it would be reasonable that it would be used logically for other applications.

Suggest digging into NSErrorRecoveryAttempting (I don't do it myself) http://apple.co/22Au1GR

Confirm the host, then select the individual URL exclusion route mentioned here. Depending on the implementation, it may also make sense to store the host as an exception for future reference.

This is similar to what Apple implemented by nature in Cocoa, but so far I have not found the “easy button”. I would like to use the "kLetUserDecide" flag in something in the NSURL or NSURLSession, and not to everyone who needs to implement the delegation method, as well as the NSErrorRecoveryAttempting protocol.

0
Dec 31 '15 at 3:42
source share

update xcode 9

  var result:(message:String, data:Data?) = (message: "Fail", data: nil) var request = URLRequest(url: url) let sessionDelegate = SessionDelegate() let session = URLSession(configuration: .default, delegate: sessionDelegate, delegateQueue: nil) let task = session.dataTask(with: request){(data, response, error) in } task.resume() 

delegate task

  class SessionDelegate:NSObject, URLSessionDelegate { func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) { print(challenge.protectionSpace.host) if(challenge.protectionSpace.host == "111.11.11.11") { let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!) completionHandler(URLSession.AuthChallengeDisposition.useCredential, credential) } } } } 
0
Oct 18 '17 at 8:30
source share

Here is a solution that worked for me. You must accept the connection through the connection delegate, including both messages:

 - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; } 

Please note that you do not check the validity of the certificate, therefore, only SSL encryption of the HTTPS connection is interesting, but signature authority is not taken into account here, which can reduce security.

-one
Nov 09 '14 at 2:59
source share



All Articles