Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Fix for self-signed client certificates on iOS 5 (alternate) #314

Open
wants to merge 1 commit into
from
Jump to file or symbol
Failed to load files and symbols.
+111 −37
Split
View
@@ -368,8 +368,12 @@ typedef void (^ASIDataBlock)(NSData *data);
// If not nil and the URL scheme is https, CFNetwork configured to supply a client certificate
SecIdentityRef clientCertificateIdentity;
+
NSArray *clientCertificates;
+ // If not nil and the URL scheme is https, CFNetwork configured to check server certificates with ONLY this CA certificate
+ SecCertificateRef caCertificate;
+
// Details on the proxy to use - you could set these yourself, but it's probably best to let ASIHTTPRequest detect the system proxy settings
NSString *proxyHost;
int proxyPort;
@@ -440,6 +444,9 @@ typedef void (^ASIDataBlock)(NSData *data);
// Used internally to record when a request has finished downloading data
BOOL downloadComplete;
+ // Used internally to record when a request has been checked against a ca certificate
+ BOOL caCertificateCheckComplete;
+
// An ID that uniquely identifies this request - primarily used for debugging persistent connections
NSNumber *requestID;
@@ -755,6 +762,10 @@ typedef void (^ASIDataBlock)(NSData *data);
- (void)setClientCertificateIdentity:(SecIdentityRef)anIdentity;
+#pragma mark ca certificate
+
+- (void)setCaCertificate:(SecCertificateRef)aCaCertificate;
+
#pragma mark session credentials
+ (NSMutableArray *)sessionProxyCredentialsStore;
View
@@ -180,6 +180,8 @@ - (void)timeOutPACRead;
- (void)useDataFromCache;
+- (BOOL)checkCaCertificate;
+
// Called to update the size of a partial download when starting a request, or retrying after a timeout
- (void)updatePartialDownloadSize;
@@ -234,6 +236,7 @@ - (void)callBlock:(ASIBasicBlock)block;
@property (assign) ASIAuthenticationState authenticationNeeded;
@property (assign, nonatomic) BOOL readStreamIsScheduled;
@property (assign, nonatomic) BOOL downloadComplete;
+@property (assign, nonatomic) BOOL caCertificateCheckComplete;
@property (retain) NSNumber *requestID;
@property (assign, nonatomic) NSString *runLoopMode;
@property (retain, nonatomic) NSTimer *statusTimer;
@@ -344,6 +347,9 @@ - (void)dealloc
if (clientCertificateIdentity) {
CFRelease(clientCertificateIdentity);
}
+ if (caCertificate) {
+ CFRelease(caCertificate);
+ }
[self cancelLoad];
[redirectURL release];
[statusTimer invalidate];
@@ -1205,44 +1211,41 @@ - (void)startRequest
// Handle SSL certificate settings
//
- if([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) {
-
- // Tell CFNetwork not to validate SSL certificates
- if (![self validatesSecureCertificate]) {
- // see: http://iphonedevelopment.blogspot.com/2010/05/nsstream-tcp-and-ssl.html
-
- NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
- [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
- kCFNull,kCFStreamSSLPeerName,
- nil];
-
- CFReadStreamSetProperty((CFReadStreamRef)[self readStream],
- kCFStreamPropertySSLSettings,
- (CFTypeRef)sslProperties);
- [sslProperties release];
- }
-
- // Tell CFNetwork to use a client certificate
- if (clientCertificateIdentity) {
- NSMutableDictionary *sslProperties = [NSMutableDictionary dictionaryWithCapacity:1];
-
- NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[clientCertificates count]+1];
-
- // The first object in the array is our SecIdentityRef
- [certificates addObject:(id)clientCertificateIdentity];
-
- // If we've added any additional certificates, add them too
- for (id cert in clientCertificates) {
- [certificates addObject:cert];
+ if([[[[self url] scheme] lowercaseString] isEqualToString:@"https"])
+ {
+ NSMutableDictionary *sslProperties = [NSMutableDictionary dictionaryWithCapacity:1];
+
+ // Tell CFNetwork not to validate SSL certificates
+ if ((!validatesSecureCertificate) || (caCertificate))
+ {
+ [sslProperties setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCFStreamSSLValidatesCertificateChain];
+ if (caCertificate)
+ {
+ [self setCaCertificateCheckComplete:NO];
+ }
}
-
- [sslProperties setObject:certificates forKey:(NSString *)kCFStreamSSLCertificates];
-
- CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertySSLSettings, sslProperties);
- }
-
+ else
+ {
+ [sslProperties setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLValidatesCertificateChain];
+ }
+
+ // Tell CFNetwork to use a client certificate
+ if (clientCertificateIdentity)
+ {
+ NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[clientCertificates count]+1];
+
+ // The first object in the array is our SecIdentityRef
+ [certificates addObject:(id)clientCertificateIdentity];
+
+ // If we've added any additional certificates, add them too
+ for (id cert in clientCertificates)
+ {
+ [certificates addObject:cert];
+ }
+ [sslProperties setObject:certificates forKey:(NSString *)kCFStreamSSLCertificates];
+ }
+
+ CFReadStreamSetProperty((CFReadStreamRef)readStream, kCFStreamPropertySSLSettings, sslProperties);
}
//
@@ -1640,6 +1643,7 @@ - (ASIHTTPRequest *)HEADRequest
[headRequest setUseHTTPVersionOne:[self useHTTPVersionOne]];
[headRequest setValidatesSecureCertificate:[self validatesSecureCertificate]];
[headRequest setClientCertificateIdentity:clientCertificateIdentity];
+ [headRequest setCaCertificate:caCertificate];
[headRequest setClientCertificates:[[clientCertificates copy] autorelease]];
[headRequest setPACurl:[self PACurl]];
[headRequest setShouldPresentCredentialsBeforeChallenge:[self shouldPresentCredentialsBeforeChallenge]];
@@ -3242,6 +3246,11 @@ - (BOOL)willAskDelegateToConfirmRedirect
- (void)handleBytesAvailable
{
+ if (![self checkCaCertificate])
+ {
+ return;
+ }
+
if (![self responseHeaders]) {
[self readResponseHeaders];
}
@@ -3647,6 +3656,43 @@ - (BOOL)retryUsingNewConnection
return NO;
}
+- (BOOL)checkCaCertificate
+{
+ BOOL success = YES;
+ if (([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) &&
+ (caCertificate) &&
+ (![self caCertificateCheckComplete]))
+ {
+ SecPolicyRef policy = SecPolicyCreateSSL(NO, (CFStringRef)[[self url] host]);
+ SecTrustRef trust = NULL;
+ CFArrayRef streamCertificates =
+ (CFArrayRef)[readStream propertyForKey:(NSString*)kCFStreamPropertySSLPeerCertificates];
+ SecTrustCreateWithCertificates(streamCertificates,
+ policy,
+ &trust);
+ SecTrustSetAnchorCertificates(trust, (CFArrayRef)[NSArray arrayWithObject:(id) caCertificate]);
+ SecTrustResultType trustResultType = kSecTrustResultInvalid;
+ OSStatus status = SecTrustEvaluate(trust, &trustResultType);
+ if ((status != errSecSuccess) || (trustResultType != kSecTrustResultUnspecified))
+ {
+ NSString *reason = @"A connection failure occurred: SSL problem (certificate failed check against user assigned ca)";
+ [self cancelLoad];
+ [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain
+ code:ASIConnectionFailureErrorType
+ userInfo:[NSDictionary dictionaryWithObjectsAndKeys:reason,NSLocalizedDescriptionKey,nil]]];
+ success = NO;
+ }
+ if (trust) {
+ CFRelease(trust);
+ }
+ if (policy) {
+ CFRelease(policy);
+ }
+ [self setCaCertificateCheckComplete:YES];
+ }
+ return success;
+}
+
- (void)handleStreamError
{
@@ -4091,6 +4137,7 @@ - (id)copyWithZone:(NSZone *)zone
[newRequest setShouldRedirect:[self shouldRedirect]];
[newRequest setValidatesSecureCertificate:[self validatesSecureCertificate]];
[newRequest setClientCertificateIdentity:clientCertificateIdentity];
+ [newRequest setCaCertificate:caCertificate];
[newRequest setClientCertificates:[[clientCertificates copy] autorelease]];
[newRequest setPACurl:[self PACurl]];
[newRequest setShouldPresentCredentialsBeforeChallenge:[self shouldPresentCredentialsBeforeChallenge]];
@@ -4130,6 +4177,21 @@ - (void)setClientCertificateIdentity:(SecIdentityRef)anIdentity {
}
+#pragma mark ca certificate
+
+- (void)setCaCertificate:(SecCertificateRef)aCaCertificate {
+ if(caCertificate) {
+ CFRelease(caCertificate);
+ }
+
+ caCertificate = aCaCertificate;
+
+ if (caCertificate) {
+ CFRetain(caCertificate);
+ }
+}
+
+
#pragma mark session credentials
+ (NSMutableArray *)sessionProxyCredentialsStore
@@ -5100,6 +5162,7 @@ - (void)setRequestRedirectedBlock:(ASIBasicBlock)aRedirectBlock
@synthesize readStreamIsScheduled;
@synthesize shouldUseRFC2616RedirectBehaviour;
@synthesize downloadComplete;
+@synthesize caCertificateCheckComplete;
@synthesize requestID;
@synthesize runLoopMode;
@synthesize statusTimer;