From b1f9598ed24b1ec1b6f24b004578e6736590998b Mon Sep 17 00:00:00 2001 From: Oliver Letterer Date: Fri, 28 Dec 2012 11:48:42 +0100 Subject: [PATCH] Adds SSLPinningMode to AFURLConnectionOperation and defaultSSLPinMode to AFHTTPClient. --- AFNetworking/AFHTTPClient.h | 8 ++++ AFNetworking/AFHTTPClient.m | 4 ++ AFNetworking/AFURLConnectionOperation.h | 14 ++++++ AFNetworking/AFURLConnectionOperation.m | 64 ++++++++++++++++++++++--- 4 files changed, 83 insertions(+), 7 deletions(-) diff --git a/AFNetworking/AFHTTPClient.h b/AFNetworking/AFHTTPClient.h index 68c6de8..79234b2 100644 --- a/AFNetworking/AFHTTPClient.h +++ b/AFNetworking/AFHTTPClient.h @@ -23,6 +23,7 @@ #import #import +#import "AFURLConnectionOperation.h" /** `AFHTTPClient` captures the common patterns of communicating with an web application over HTTP. It encapsulates information like base URL, authorization credentials, and HTTP headers, and uses them to construct and manage the execution of HTTP request operations. @@ -137,6 +138,13 @@ typedef enum { @property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; #endif +/** + Default SSL pinning mode for each `AFHTTPRequestOperation` which will be enqueued with `enqueueHTTPRequestOperation:`. + */ +#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_ +@property (nonatomic, assign) AFURLConnectionOperationSSLPinningMode defaultSSLPinMode; +#endif + ///--------------------------------------------- /// @name Creating and Initializing HTTP Clients ///--------------------------------------------- diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index 3fec759..ceda84d 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -524,6 +524,10 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) { #pragma mark - - (void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation { +#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_ + operation.SSLPinningMode = self.defaultSSLPinMode; +#endif + [self.operationQueue addOperation:operation]; } diff --git a/AFNetworking/AFURLConnectionOperation.h b/AFNetworking/AFURLConnectionOperation.h index 94c07e8..47139ac 100644 --- a/AFNetworking/AFURLConnectionOperation.h +++ b/AFNetworking/AFURLConnectionOperation.h @@ -24,6 +24,13 @@ #import +#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_ +typedef enum { + AFURLConnectionOperationSSLPinningModeCertificate = 0, + AFURLConnectionOperationSSLPinningModePublicKey +} AFURLConnectionOperationSSLPinningMode; +#endif + /** `AFURLConnectionOperation` is a subclass of `NSOperation` that implements `NSURLConnection` delegate methods. @@ -124,6 +131,13 @@ */ @property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding; +/** + The pinning mode which will be used for SSL connections. + */ +#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_ +@property (nonatomic, assign) AFURLConnectionOperationSSLPinningMode SSLPinningMode; +#endif + ///------------------------ /// @name Accessing Streams ///------------------------ diff --git a/AFNetworking/AFURLConnectionOperation.m b/AFNetworking/AFURLConnectionOperation.m index 0d27490..0a590a2 100644 --- a/AFNetworking/AFURLConnectionOperation.m +++ b/AFNetworking/AFURLConnectionOperation.m @@ -192,6 +192,36 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat return _pinnedCertificates; } ++ (NSArray *)pinnedPublicKeys { + static NSArray *_pinnedPublicKeys = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + NSArray *pinnedCertificates = [self pinnedCertificates]; + NSMutableArray *publicKeys = [NSMutableArray array]; + + for (NSData *data in pinnedCertificates) { + SecCertificateRef allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)data); + NSParameterAssert(allowedCertificate); + + SecPolicyRef policy = SecPolicyCreateBasicX509(); + SecTrustRef allowedTrust = NULL; + OSStatus status = SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust); + NSAssert(status == noErr, @"SecTrustCreateWithCertificates error: %ld", status); + + SecKeyRef allowedPublicKey = SecTrustCopyPublicKey(allowedTrust); + [publicKeys addObject:(__bridge_transfer id)allowedPublicKey]; + + CFRelease(allowedTrust); + CFRelease(allowedCertificate); + } + + _pinnedPublicKeys = [[NSArray alloc] initWithArray:publicKeys]; + }); + + return _pinnedPublicKeys; +} + - (id)initWithRequest:(NSURLRequest *)urlRequest { self = [super init]; if (!self) { @@ -499,14 +529,34 @@ willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challe { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; - SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0); - NSData *certificateData = (__bridge_transfer NSData *)SecCertificateCopyData(certificate); - if ([[[self class] pinnedCertificates] containsObject:certificateData]) { - NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; - [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; - } else { - [[challenge sender] cancelAuthenticationChallenge:challenge]; + switch (self.SSLPinningMode) { + case AFURLConnectionOperationSSLPinningModeCertificate: { + SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0); + NSData *certificateData = (__bridge_transfer NSData *)SecCertificateCopyData(serverCertificate); + + if ([[[self class] pinnedCertificates] containsObject:certificateData]) { + NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; + [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; + } else { + [[challenge sender] cancelAuthenticationChallenge:challenge]; + } + break; + } case AFURLConnectionOperationSSLPinningModePublicKey: { + id publicKey = (__bridge_transfer id)SecTrustCopyPublicKey(serverTrust); + + for (id allowedPublicKey in [self.class pinnedPublicKeys]) { + if ([allowedPublicKey isEqual:publicKey]) { + NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; + [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; + + return; + } + } + + [[challenge sender] cancelAuthenticationChallenge:challenge]; + break; + } } } }