From 5d25e06eaaf91ba7201bff147358d9780025dfdb Mon Sep 17 00:00:00 2001 From: Constantin Lungu Date: Tue, 2 Aug 2016 17:45:02 +0300 Subject: [PATCH] Made SSL pinning disableable via SEAPIRequestManager --- Classes/API/SEAPIRequestManager.h | 23 ++++++++++++++++++- Classes/API/SEAPIRequestManager.m | 19 +++++++++++++++ .../API/SERequestHandlerURLSessionDelegate.m | 19 +++++++++++---- Salt Edge API Demo/AppDelegate.m | 1 + 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/Classes/API/SEAPIRequestManager.h b/Classes/API/SEAPIRequestManager.h index f7c3ff5..959e7ff 100644 --- a/Classes/API/SEAPIRequestManager.h +++ b/Classes/API/SEAPIRequestManager.h @@ -29,6 +29,11 @@ typedef void (^SEAPIRequestFailureBlock)(SEError* error); +typedef NS_ENUM(NSUInteger, SEAPIRequestManagerSSLPinningMode) { + SEAPIRequestManagerSSLPinningModeEnabled = 0, + SEAPIRequestManagerSSLPinningModeDisabled +}; + /** SEAPIRequestManager is a class designed to provide convenient methods in communicating with the Salt Edge API. */ @@ -39,6 +44,23 @@ typedef void (^SEAPIRequestFailureBlock)(SEError* error); */ + (instancetype)manager; +/** + Sets the given pinning mode and uses it for all outgoing API requests. + + @param mode The mode to set to. This can either be enabled or disabled. The mode is enabled by default. + @see SSLPinningEnabled + */ ++ (void)setSSLPinningMode:(SEAPIRequestManagerSSLPinningMode)mode; + +/** + Checks whether SSL pinning for outgoing API requests is enabled. + + @return YES if SSL pinning is enabled, NO if SSL pinning is disabled. + @see setSSLPinningMode: + */ + ++ (BOOL)SSLPinningEnabled; + /** Links your Client ID and App Secret to the request manager. All outgoing requests will have the proper app-related HTTP headers set by default. @@ -47,7 +69,6 @@ typedef void (^SEAPIRequestFailureBlock)(SEError* error); */ + (void)linkClientId:(NSString*)clientId appSecret:(NSString*)appSecret; - /** Links your Customer Secret to the request manager. All outgoing requests related to logins will have the proper customer-related HTTP headers set by default. diff --git a/Classes/API/SEAPIRequestManager.m b/Classes/API/SEAPIRequestManager.m index 9821b6d..bfbed20 100644 --- a/Classes/API/SEAPIRequestManager.m +++ b/Classes/API/SEAPIRequestManager.m @@ -44,6 +44,7 @@ /* HTTP Session config */ static NSDictionary* sessionHeaders; +static SEAPIRequestManagerSSLPinningMode sslPinningMode; /* Login polling */ static CGFloat const kLoginPollDelayTime = 5.0f; @@ -76,6 +77,16 @@ + (instancetype)manager return manager; } ++ (void)setSSLPinningMode:(SEAPIRequestManagerSSLPinningMode)mode +{ + sslPinningMode = mode; +} + ++ (BOOL)SSLPinningEnabled +{ + return sslPinningMode == SEAPIRequestManagerSSLPinningModeEnabled; +} + + (void)linkClientId:(NSString *)clientId appSecret:(NSString *)appSecret { NSAssert(sessionHeaders == nil, @"Session headers are already set up."); @@ -584,6 +595,14 @@ - (void)requestRefreshTokenForLoginSecret:(NSString*)loginSecret #pragma mark - #pragma mark - Private API ++ (void)initialize +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sslPinningMode = SEAPIRequestManagerSSLPinningModeEnabled; + }); +} + - (instancetype)init { if (self = [super init]) { diff --git a/Classes/API/SERequestHandlerURLSessionDelegate.m b/Classes/API/SERequestHandlerURLSessionDelegate.m index 1075275..4f7c1dd 100644 --- a/Classes/API/SERequestHandlerURLSessionDelegate.m +++ b/Classes/API/SERequestHandlerURLSessionDelegate.m @@ -7,6 +7,7 @@ // #import "SERequestHandlerURLSessionDelegate.h" +#import "SEAPIRequestManager.h" @implementation SERequestHandlerURLSessionDelegate @@ -19,7 +20,7 @@ + (instancetype)sharedInstance dispatch_once(&onceToken, ^{ _sharedInstance = [[self alloc] init]; NSString* cerPath = [[NSBundle mainBundle] pathForResource:@"saltedge.com" ofType:@"cer"]; - NSAssert(cerPath != nil, @"The saltedge.com SSL certificate could not be located in the app bundle."); + NSAssert(cerPath != nil, @"The saltedge.com SSL certificate could not be located in the app bundle. SSL pinning will not be possible without it."); }); return _sharedInstance; } @@ -29,15 +30,25 @@ + (instancetype)sharedInstance - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; + + void (^useChallengeCredential)() = ^{ + NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; + [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; + completionHandler(NSURLSessionAuthChallengeUseCredential, credential); + }; + + if (![SEAPIRequestManager SSLPinningEnabled]) { + useChallengeCredential(); + return; + } + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0); NSData* remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate)); NSString* cerPath = [[NSBundle mainBundle] pathForResource:@"saltedge.com" ofType:@"cer"]; NSData* localCertificateData = [NSData dataWithContentsOfFile:cerPath]; if ([remoteCertificateData isEqualToData:localCertificateData]) { - NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; - [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; - completionHandler(NSURLSessionAuthChallengeUseCredential, credential); + useChallengeCredential(); } else { [[challenge sender] cancelAuthenticationChallenge:challenge]; completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); diff --git a/Salt Edge API Demo/AppDelegate.m b/Salt Edge API Demo/AppDelegate.m index 04dbe2a..bee857a 100644 --- a/Salt Edge API Demo/AppDelegate.m +++ b/Salt Edge API Demo/AppDelegate.m @@ -49,6 +49,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( return YES; } [SEAPIRequestManager linkClientId:clientId appSecret:appSecret]; + [SEAPIRequestManager setSSLPinningMode:SEAPIRequestManagerSSLPinningModeEnabled]; // No need to actually write this since SSL pinning is enabled by default. void (^setWindowRootViewController)() = ^() { self.tabBar = [[TabBarVC alloc] init];