NSURLConnection ignoring SSL Certificate? - swift

I know this question is asked often, but I have implemented the solution found in the answers and I'm not having any luck. I am not a Swift developer, so I'm guessing I'm missing something.
Here is my NSURLConnectionDelegate methods:
func connection(connection: NSURLConnection, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace) -> Bool {
return protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
}
func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
print("Second attempt resulted in authentication challenge")
challenge.sender!.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!), forAuthenticationChallenge: challenge)
challenge.sender!.continueWithoutCredentialForAuthenticationChallenge(challenge)
}
func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
print("We got a response.....")
}
func connection(connection: NSURLConnection, didFailWithError error: NSError) {
print("============ second attempt failed ==============")
print("\(error)")
}
The connection goes through a VPN that doesn't allow for DNS. We have to use the IP, which results in an invalid cert error. The Cert is correct, just not using the IP. I am trying to ignore the cert error but I am still getting "An SSL error has occurred and a secure connection to the server cannot be made"
My understanding is that
challenge.sender!.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!), forAuthenticationChallenge: challenge)
challenge.sender!.continueWithoutCredentialForAuthenticationChallenge(challenge)
Should resolve this, but it doesn't seem to be working. The print line does run, so the delegate method is being called.
Thanks!

As Breek suggested, NSAppTransportSecurity in info.plist did the trick for iOS 9.

Related

URLProtocol doesn't get initialized

I am implementing an URLProtocol in an app.
import Cocoa
class MyURLProtocol: URLProtocol {
override init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
super.init(request: request, cachedResponse: cachedResponse, client: client)
}
override class func canInit(with request: URLRequest) -> Bool {
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override func startLoading() {
print("loading")
}
}
Although canInit(with request: URLRequest) always returns true, neither init(…) nor canonicalRequest(…) nor startLoading() get called.
URLProtocol.registerClass for MyURLProtocol is called in willFinishLaunching in the AppDelegate
I don't know what to do. Yesterday, day code called at least the functions.
Thanks for your help.
Are you using URLSession? URLSession bypasses the normal protocol registration and instead has you explicitly configure the protocols in the URLSessionConfiguration. See URLSessionConfiguration.protocolClasses.
Actually I'am working on a macOS app and not an iOS one, but it has fixed the problem when I was changing from WKWebView to WebView.
Thanks to Kevin Ballard on his comment.

Unable to get error description/code for SKProductsRequest

MAIN QUESTION:
Why func request(_ request: SKRequest, didFailWithError error: Error) is never get called?
TEMPORARY SOLUTION:
First of all. I know that I can understand that it fails calculating response.products.count and compare to zero. But I want to get exact error code, not just "fail event" with no code and human readable description.
DESCRIPTION:
I'm loading purchases list using such come:
let request = SKProductsRequest(productIdentifiers:Set(/*init the Set with strings*/))
self.productsRequest = request
request.delegate = self
request.start()
Also I've implemented 3 methods of SKProductsRequestDelegate
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
func requestDidFinish(_ request: SKRequest) {
func request(_ request: SKRequest, didFailWithError error: Error) {
The first two are always get called, even if WiFi is turned off.
But the last one is never get called.
For more info: SKRequestDelegate, and its base protocol SKRequestDelegate declarations
public protocol SKProductsRequestDelegate : SKRequestDelegate {
// Sent immediately before -requestDidFinish:
#available(OSX 10.7, *)
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse)
}
public protocol SKRequestDelegate : NSObjectProtocol {
#available(OSX 10.7, *)
optional public func requestDidFinish(_ request: SKRequest)
#available(OSX 10.7, *)
optional public func request(_ request: SKRequest, didFailWithError error: Error)
}
P. S.
I've tried to google about it, but I've found only problems like:SKProductsRequest delegate methods are never called. But it is not mine.

NSURLConnection to NSURLSession

I'm new to Swift and NSURLConnection & NSURLSession. I have this code, to load a webpage and this works. But I got this warning that NSURLConnection was deprecated in iOS 9.0, and I have to use NSURLSession.
This is my code:
var authenticated:Bool = false
var urlConnection:NSURLConnection!
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if !authenticated {
self.authenticated = false
self.urlConnection = NSURLConnection(request: request, delegate: self)!
urlConnection.start()
return false
}
return true
}
// We use this method is to accept an untrusted site which unfortunately we need to do, as our PVM servers are self signed.
func connection(connection: NSURLConnection, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace) -> Bool {
return (protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust)
}
func connection(connection: NSURLConnection, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
if challenge.previousFailureCount == 0 {
self.authenticated = true
let credential: NSURLCredential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!
)
challenge.sender!.useCredential(credential, forAuthenticationChallenge: challenge)
}
else {
challenge.sender!.cancelAuthenticationChallenge(challenge)
}
}
func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
// remake a webview call now that authentication has passed ok.
self.authenticated = true
web.loadRequest(request)
// Cancel the URL connection otherwise we double up (webview + url connection, same url = no good!)
urlConnection.cancel()
}
This works. Now I want to 'convert' it to NSURLSession, but I can't seem to manage. Can somebody help me with this? I'm quite sure it's not so difficult for someone who can code very well.
I've tried several times to change to NSURLSession, but every time I've got this error: NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813). And with NSURLConnection the problem is solved.
This is one of my attempts while using NSURLSession:
var authenticated:Bool = false
var urlSession:NSURLSession!
var urlSessionConfig:NSURLSessionConfiguration!
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if !authenticated {
self.authenticated = false
self.urlSessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
self.urlSession = NSURLSession(configuration: self.urlSessionConfig, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
let task = self.urlSession.dataTaskWithRequest(request){
(myData, myResponse, myError) -> Void in
if(myError == nil){
if(self.authenticated){
self.web.loadRequest(request)
}
}
}
task.resume()
return false
}
return true
}
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust){
if(challenge.protectionSpace.host == "mydomain.org"){
self.authenticated = true
let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential,credential);
}
}
}
You don't have to use NSURLSession. Odds are, NSURLConnection will be in a semi-supported state until the end of time, given how broadly it is used.
With that said, I'm only going to say this once, so listen very carefully. Do not, under any circumstances, publicly ship either version of this code as written. Self-signed certificates are okay, as long as you check them properly. This code is not doing that, which makes them no better than HTTP.
If the data has to be kept secret, either use a real certificate or add code to validate the self-signed certificates properly.
If it isn't even slightly important to keep it secret, just use HTTP.
This document explains how to properly implement modified TLS chain validation by adding trust for a specific certificate:
https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
BTW, you're not doing anything with challenges in other protection spaces or for other hostnames. If you never call the completion handler, then tasks that request any other type of authentication will just hang around forever, in limbo, waiting for you to decide how to handle the authentication request. This is probably not what you want, though I doubt it is causing your problem unless you're behind a proxy. Your NSURLConnection code accepted challenges within a single protection space, so it probably didn't experience that problem (as much).
With that said, I don't see why this is failing with that error code unless there's something else wrong with the cert beyond being self-signed. Oh, and there's a missing underscore in the method declaration. I don't think that is important anymore, but I'm not certain. Be sure your delegate method is actually getting called.
You might also try setting the CFNETWORK_DIAGNOSTICS environment variable to 1 (or more) and see if that provides any further insight.

Is this specialized use of NSURLConnection to handle self signed certs convertible to NSURLSession?

I have a self signed certificate in the VM I use to test my service. Using answers found in UIWebView to view self signed websites (No private api, not NSURLConnection) - is it possible? I was able to write functioning swift 2.0 code. Xcode 7 tells me that NSURLConnection is deprecated and I should use NSURLSession instead. None of my attempts to migrate this code succeeded, and none of the usual conversion scenarios described in other answers seem to apply.
If I create a new NSURLSession in order to handle the authentication challenge with my delegate methods, the other loads still happen on the sharedSession, and therefore fail.
var authRequest : NSURLRequest? = nil
var authenticated = false
var trustedDomains = [:] // set up as necessary
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if !authenticated {
authRequest = request
let urlConnection: NSURLConnection = NSURLConnection(request: request, delegate: self)!
urlConnection.start()
return false
}
else if isWebContent(request.URL!) { // write your method for this
return true
}
return processData(request) // write your method for this
}
func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
let challengeHost = challenge.protectionSpace.host
if let _ = trustedDomains[challengeHost] {
challenge.sender!.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!), forAuthenticationChallenge: challenge)
}
}
challenge.sender!.continueWithoutCredentialForAuthenticationChallenge(challenge)
}
func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
authenticated = true
connection.cancel()
webview!.loadRequest(authRequest!)
}
I was able to suppress the deprecation warning by adding this line before the first method. I would prefer a solution that replaces NSURLConnection but in the absence of that, this will have to do.
// hide NSURLConnection deprecation
#available(iOS, deprecated=9.0)

NSURLConnection The certificate for this server is invalid

Recently I've been trying to connect to a test server of mine using a self-signed SSL certificate using NSURLSession.sharedSession().dataTaskWithRequest()
Now I get this error:
The certificate for this server is invalid. You might be connecting to a server that is pretending to be “...” which could put your confidential information at risk.
I've been searching the web for how to solve it.
All of them advised for using one of these:
func connection(connection: NSURLConnection, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace) -> Bool
func connection(connection: NSURLConnection, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge)
func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge)
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void)
Now, when I ran my app I noticed that only
func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge)
ran.
This is my code:
func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge)
{
// Trusting and not trusting connection to host: Self-signed certificate
challenge.sender.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust), forAuthenticationChallenge: challenge)
challenge.sender.continueWithoutCredentialForAuthenticationChallenge(challenge)
if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust)
{
let trust = challenge.protectionSpace.serverTrust
let cred = NSURLCredential(forTrust: trust)
challenge.sender.useCredential(cred, forAuthenticationChallenge: challenge)
}
}
Yet I keep getting NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813).
Aha! Apparently I've had a let urlConnection = NSURLConnection(request: request, delegate: self) a bit before that I haven't noticed...
And I changed NSURLSession.sharedSession().dataTaskWithRequest()
to
let task = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: self, delegateQueue: NSOperationQueue.mainQueue()).dataTaskWithRequest(request)
And left in place the delegate methods... that did it :)