views:

2728

answers:

7

Using NSURLRequest, I am trying to access a web site that has an expired certificate. When I send the request, my connection:didFailWithError delegate method is invoked with the following info:

-1203, NSURLErrorDomain, bad server certificate

My searches have only turned up one solution: a hidden class method in NSURLRequest:

[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:myHost];

However, I don't want to use private APIs in a production app for obvious reasons.

Any suggestions on what to do? Do I need to use CFNetwork APIs, and if so, two questions:

  • Any sample code I can use to get started? I haven't found any online.
  • If I use CFNetwork for this, do I have to ditch NSURL entirely?

EDIT:

iPhone OS 3.0 introduced a supported method for doing this. More details here: http://stackoverflow.com/questions/933331/how-to-use-nsurlconnection-to-connect-with-ssl-for-an-untrusted-cert

A: 

I've hit the same issue - I was developing a SOAP client, and the dev server has a "homegrown" certificate. I wasn't able to solve the issue even using that method, since I wasn't using NSURL, but the (poorly documented and apparently abandoned) WS methods, and decided for the time being to (internally) just use a non-SSL connection.

Having said that, however, the question that springs to mind is, if you aren't willing to use a private API in a production app, should you be allowing access to a site with a dodgy certificate?

I'll quote Jens Alfke:

That's not just a theoretical security problem. Something
like 25% of public DNS servers have been compromised, according to
recent reports, and can direct users to phishing/malware/ad sites even
if they enter the domain name properly. The only thing protecting you
from that is SSL certificate checking.

Matthew Schinckel
+1  A: 

@Matthew Schinckel

Having said that, however, the question that springs to mind is, if you aren't willing to use a private API in a production app, should you be allowing access to a site with a dodgy certificate?

The site in question is in our testing environment, only accessible internally. I guess the network admins are too cheap or too lazy to maintain valid certs for it. Our production environment has a valid certificate, of course.

As this certificate-dodging feature would not be enabled or needed in production, I could use the private API just for testing. I'd still prefer to do it the "right" way, since I'd be back at square one if the private API was ever yanked.

Mike McMaster
A: 

Can you create a self signed certificate and add your custom certificate authority to the trusted CAs? I'm not quite sure how this would work on the iPhone, but I'd assume on Mac OS X you would add these to the Keychain.

You may also be interested in this post Re: How to handle bad certificate error in NSURLDownload

David Schlosnagle
+2  A: 

If it's for an internal server for testing purposes, why not just import the test server's certificate into the KeyChain and set custom trust settings?

Graham Lee
+3  A: 

The supported way of doing this requires using CFNetwork. You have to do is attach a kCFStreamPropertySSLSettings to the stream that specifies kCFStreamSSLValidatesCertificateChain == kCFBooleanFalse. Below is some quick code that does it, minus checking for valid results add cleaning up. Once you have done this You can use CFReadStreamRead() to get the data.

CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://www.apple.com"), NULL);
CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), myURL, kCFHTTPVersion1_1);
CFReadStreamRef myStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest);
CFMutableDictionaryRef myDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(myDict, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
CFReadStreamSetProperty(myStream, kCFStreamPropertySSLSettings, myDict);    
CFReadStreamOpen(myStream);
Louis Gerbarg
A: 

Another option would be to use an alternate connection library.

I am a huge fan of AsyncSocket and it has support for self signed certs

http://code.google.com/p/cocoaasyncsocket/

Take a look, I think it is way more robust then the standard NSURLRequests.

chews
+2  A: 

iPhone OS 3.0 introduced a supported way of doing this that doesn't require the lower-level CFNetwork APIs. More details here:

http://stackoverflow.com/questions/933331/how-to-use-nsurlconnection-to-connect-with-ssl-for-an-untrusted-cert

Mike McMaster