Self-signed SSL certificates and stringWithContentsOfURL
I am requesting some data from a server with NSString#stringWithContentsOfURL:
. The server is using a self-signed certificate, so stringWithContentsOfURL:
simply returns nil and does not give a change to accept the certificate.
This is all expected behaviour. I know how to properly do this with NSURLConnection
and it's delegate methods instead, but I am looking for a shorter term fix instead of rewriting this code. (Yay deadlines)
So my question is, is it possible to import the self-signed certificate into the application's keychain, and will that cause st开发者_Python百科ringWithContentsOfURL:
to accept the self-signed certificate?
I beleive that there is an enterprise tool from Apple for installing SSL certs -- it was necessary to get exchange mail working.
You may also be able to mail the .cer file to yourself, open on your iPhone and install it.
There is also the AdvancedURLConnections sample project from Apple
This sample demonstrates various advanced networking techniques with NSURLConnection. Specifically, it demonstrates how to respond to authentication challenges, how to modify the default server trust evaluation (for example, to support a server with a self-signed certificate), and how to provide client identities.
There is no way to go around that SSL verification scheme, and after a while of trying and looking all over the place for a solution, I had to implement a class to do just that.
Since the advantage of NSString's stringWithContentsOfURL is to be synchronous, I had to make sure mine was as well. It's probably a little big for every purpose, but you get the gist of it.
@interface NZStringLoader : NSObject
@property (assign, readonly, nonatomic) BOOL done;
@property (strong, readonly, nonatomic) NSString *result;
@property (strong, readonly, nonatomic) NSURLConnection *conn;
- (id) initWithURL:(NSURL*)u;
- (void) loadSynchronously;
@end
@implementation NZStringLoader
@synthesize done = _done, result = _result, conn = _connection;
- (id) initWithURL:(NSURL*)u {
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:u
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:10.0];
_connection = [NSURLConnection connectionWithRequest:req delegate:self];
_done = NO;
return self;
}
- (void) loadSynchronously {
[_connection start];
while(!_done)
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.15]];
}
#pragma mark -
#pragma mark NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
_done = YES;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
RTLog(@"%@", [error localizedDescription]);
_done = YES;
}
- (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];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if(_result == nil)
_result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
else
_result = [_result stringByAppendingString:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
}
@end
And it's used like this:
NZStringLoader *sl = [[NSStringLoader alloc] initWithURL:u];
[sl loadSynchronously];
result = sl.result;
I guess I could make it just one call, if I wanted to.
精彩评论