Adds SSLPinningMode to AFURLConnectionOperation and defaultSSLPinMode to AFHTTPClient.

This commit is contained in:
Oliver Letterer 2012-12-28 11:48:42 +01:00
parent 121ef7afa8
commit b1f9598ed2
4 changed files with 83 additions and 7 deletions

View file

@ -23,6 +23,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <Availability.h> #import <Availability.h>
#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. `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; @property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
#endif #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 /// @name Creating and Initializing HTTP Clients
///--------------------------------------------- ///---------------------------------------------

View file

@ -524,6 +524,10 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
#pragma mark - #pragma mark -
- (void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation { - (void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation {
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
operation.SSLPinningMode = self.defaultSSLPinMode;
#endif
[self.operationQueue addOperation:operation]; [self.operationQueue addOperation:operation];
} }

View file

@ -24,6 +24,13 @@
#import <Availability.h> #import <Availability.h>
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
typedef enum {
AFURLConnectionOperationSSLPinningModeCertificate = 0,
AFURLConnectionOperationSSLPinningModePublicKey
} AFURLConnectionOperationSSLPinningMode;
#endif
/** /**
`AFURLConnectionOperation` is a subclass of `NSOperation` that implements `NSURLConnection` delegate methods. `AFURLConnectionOperation` is a subclass of `NSOperation` that implements `NSURLConnection` delegate methods.
@ -124,6 +131,13 @@
*/ */
@property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding; @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 /// @name Accessing Streams
///------------------------ ///------------------------

View file

@ -192,6 +192,36 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
return _pinnedCertificates; 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 { - (id)initWithRequest:(NSURLRequest *)urlRequest {
self = [super init]; self = [super init];
if (!self) { if (!self) {
@ -499,14 +529,34 @@ willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challe
{ {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
NSData *certificateData = (__bridge_transfer NSData *)SecCertificateCopyData(certificate);
if ([[[self class] pinnedCertificates] containsObject:certificateData]) { switch (self.SSLPinningMode) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; case AFURLConnectionOperationSSLPinningModeCertificate: {
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
} else { NSData *certificateData = (__bridge_transfer NSData *)SecCertificateCopyData(serverCertificate);
[[challenge sender] cancelAuthenticationChallenge:challenge];
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;
}
} }
} }
} }