Wednesday, May 30, 2012

How to use NSURLConnection to connect with SSL for an untrusted cert?


I have the following simple code to connect to a SSL webpage




NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];
[ NSURLConnection sendSynchronousRequest: urlRequest returningResponse: nil error: &error ];



Except it gives an error if the cert is a self signed one Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate". Is there a way to set it to accept connections anyway (just like in a browser you can press accept) or a way to bypass it?


Source: Tips4all

7 comments:

  1. There is a supported API for accomplishing this! Add something like this to your NSURLConnection delegate:

    - (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])
    if ([trustedHosts containsObject:challenge.protectionSpace.host])
    [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];

    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
    }


    Note that connection:didReceiveAuthenticationChallenge: can send its message to challenge.sender (much) later, after presenting a dialog box to the user if necessary, etc.

    ReplyDelete
  2. If you're unwilling (or unable) to use private APIs, there's an open source (BSD license) library called ASIHTTPRequest that provides a wrapper around the lower-level CFNetwork APIs. They recently introduced the ability to allow HTTPS connections using self-signed or untrusted certificates with the -setValidatesSecureCertificate: API. If you don't want to pull in the whole library, you could use the source as a reference for implementing the same functionality yourself.

    ReplyDelete
  3. I can't take any credit for this, but this one I found worked really well for my needs. "shouldAllowSelfSignedCert" is my BOOL variable. Just add to your NSURLConnection delegate and you should be rockin for a quick bypass on a per connection basis.

    - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)space {
    if([[space authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) {
    if(shouldAllowSelfSignedCert) {
    return YES; // Self-signed cert will be accepted
    } else {
    return NO; // Self-signed cert will be rejected
    }
    // Note: it doesn't seem to matter what you return for a proper SSL cert
    // only self-signed certs
    }
    // If no other authentication is required, return NO for everything else
    // Otherwise maybe YES for NSURLAuthenticationMethodDefault and etc.
    return NO;
    }

    ReplyDelete
  4. Ideally, there should only be two scenarios of when an iOS application would need to accept an un-trusted certificate.

    Scenario A: You are connected to a test environment which is using a self-signed certificate.

    Scenario B: You are Proxying HTTPS traffic using a MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc. The Proxies will return a certificate signed by a self-signed CA so that the proxy is able to capture HTTPS traffic.

    Production hosts should never use un-trusted certificates for obvious reasons.

    If you need to have the iOS simulator accept an un-trusted certificate for testing purposes it is highly recommended that you do not change application logic in order disable the built in certificate validation provided by the NSURLConnection APIs. If the application is released to the public without removing this logic, it will be susceptible to man-in-the-middle attacks.

    The recommended way to accept un-trusted certificates for testing purposes is to import the Certificate Authority(CA) certificate which signed the certificate onto your iOS Simulator or iOS device. I wrote up a quick blog post which demonstrates how to do this which an iOS Simulator at:

    http://blog.gdssecurity.com/labs/2011/8/7/accepting-un-trusted-certificates-using-the-ios-simulator.html

    ReplyDelete
  5. NSURLRequest has a private method called setAllowsAnyHTTPSCertificate:forHost:, which will do exactly what you'd like. You could define the allowsAnyHTTPSCertificateForHost: method on NSURLRequest via a category, and set it to return YES for the host that you'd like to override.

    ReplyDelete
  6. The category workaround posted by Nathan de Vries will pass the AppStore private API checks, and is useful in cases where you do not have control of the NSUrlConnection object.
    One example is NSXMLParser which will open the URL you supply, but does not expose the NSURLRequest or NSURLConnection.

    In iOS 4 the workaround still seems to work, but only on the device, the Simulator does not invoke the allowsAnyHTTPSCertificateForHost: method anymore.

    ReplyDelete
  7. I posted some gist code (based on someone else's work which I note) that lets you properly authenticate against a self generated certificate (and how to get a free certificate - see comments bottom of Cocoanetics)

    My code is here github

    ReplyDelete