Ben Copsey

Added validatesSecureCertificate property to turn off SSL cert validation for te…

…sting with self-signed certs
... ... @@ -217,6 +217,9 @@ extern NSString* const NetworkRequestErrorDomain;
// When YES, requests will automatically redirect when they get a HTTP 30x header (defaults to YES)
BOOL shouldRedirect;
// When NO, requests will not check the secure certificate is valid (use for self-signed cerficates during development, DO NOT USE IN PRODUCTION) Default is YES
BOOL validatesSecureCertificate;
}
... ... @@ -401,4 +404,5 @@ extern NSString* const NetworkRequestErrorDomain;
@property (assign) BOOL useHTTPVersionOne;
@property (assign, readonly) unsigned long long partialDownloadSize;
@property (assign) BOOL shouldRedirect;
@property (assign) BOOL validatesSecureCertificate;
@end
... ...
... ... @@ -97,6 +97,7 @@ static NSError *ASIUnableToCreateRequestError;
[self setTimeOutSeconds:10];
[self setUseSessionPersistance:YES];
[self setUseCookiePersistance:YES];
[self setValidatesSecureCertificate:YES];
[self setRequestCookies:[[[NSMutableArray alloc] init] autorelease]];
[self setDidFinishSelector:@selector(requestFinished:)];
[self setDidFailSelector:@selector(requestFailed:)];
... ... @@ -434,6 +435,11 @@ static NSError *ASIUnableToCreateRequestError;
// Tell CFNetwork to automatically redirect for 30x status codes
CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPShouldAutoredirect, [self shouldRedirect] ? kCFBooleanTrue : kCFBooleanFalse);
// Tell CFNetwork not to validate SSL certificates
if (!validatesSecureCertificate) {
CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, [NSMutableDictionary dictionaryWithObject:(NSString *)kCFBooleanFalse forKey:(NSString *)kCFStreamSSLValidatesCertificateChain]);
}
// Set the client
CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL};
if (!CFReadStreamSetClient(readStream, kNetworkEvents, ReadStreamClientCallBack, &ctxt)) {
... ... @@ -1350,12 +1356,28 @@ static NSError *ASIUnableToCreateRequestError;
{
NSError *underlyingError = [(NSError *)CFReadStreamCopyError(readStream) autorelease];
[self cancelLoad];
[self setComplete:YES];
if (![self error]) { // We may already have handled this error
[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIConnectionFailureErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"A connection failure occurred",NSLocalizedDescriptionKey,underlyingError,NSUnderlyingErrorKey,nil]]];
NSString *reason = @"A connection failure occurred";
// We'll use a custom error message for common SSL errors, but you should always check underlying error if you want more details
if ([[underlyingError domain] isEqualToString:NSOSStatusErrorDomain]) {
if ([underlyingError code] == errSSLUnknownRootCert) {
reason = [NSString stringWithFormat:@"%@: Secure certificate had an untrusted root",reason];
} else if ([underlyingError code] == errSSLCertExpired) {
reason = [NSString stringWithFormat:@"%@: Secure certificate expired",reason];
} else if ([underlyingError code] >= -9807 || [underlyingError code] <= -9818) {
reason = [NSString stringWithFormat:@"%@: SSL problem (probably a bad certificate)",reason];
}
}
[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIConnectionFailureErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:reason,NSLocalizedDescriptionKey,underlyingError,NSUnderlyingErrorKey,nil]]];
}
[super cancel];
}
... ... @@ -1640,4 +1662,5 @@ static NSError *ASIUnableToCreateRequestError;
@synthesize authenticationRetryCount;
@synthesize updatedProgress;
@synthesize shouldRedirect;
@synthesize validatesSecureCertificate;
@end
... ...
... ... @@ -32,5 +32,5 @@
- (void)testCharacterEncoding;
- (void)testCompressedResponse;
- (void)testCompressedResponseDownloadToFile;
- (void)testSSL;
@end
... ...
... ... @@ -616,4 +616,24 @@
GHAssertTrue(success,@"Failed to correctly display increment progress for a partial download");
}
- (void)testSSL
{
NSURL *url = [NSURL URLWithString:@"https://selfsigned.allseeing-i.com"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request start];
GHAssertNotNil([request error],@"Failed to generate an error for a self-signed certificate");
// Just for testing the request generated a custom error description - don't do this! You should look at the domain / code of the underlyingError in your own programs.
BOOL success = ([[[request error] localizedDescription] isEqualToString:@"A connection failure occurred: Secure certificate had an untrusted root"]);
GHAssertTrue(success,@"Basic synchronous request failed");
// Turn off certificate validation, and try again
request = [ASIHTTPRequest requestWithURL:url];
[request setValidatesSecureCertificate:NO];
[request start];
GHAssertNil([request error],@"Failed to accept a self-signed certificate");
}
@end
... ...