Last active
August 25, 2017 10:04
-
-
Save romantomjak/e450b8b391a4bf312966 to your computer and use it in GitHub Desktop.
SSL with custom root CA certificate using AFNetworking 2.x on iOS 7 and iOS 8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CustomRootCAAPIClient.h | |
// | |
// Created by Roman Tomjak on 19/12/14. | |
// Copyright (c) 2014 Roman Tomjak. All rights reserved. | |
// | |
#import <AFHTTPSessionManager.h> | |
@interface CustomRootCAAPIClient : AFHTTPSessionManager | |
+ (instancetype)sharedInstance; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CustomRootCAAPIClient.m | |
// | |
// Created by Roman Tomjak on 19/12/14. | |
// Copyright (c) 2014 Roman Tomjak. All rights reserved. | |
// | |
#import "CustomRootCAAPIClient.h" | |
// API keys | |
static NSString * const kApiBaseURL = @"https://somewhere.com/v1/"; | |
static NSString * const kAPIApplicationId = @"YOUR_APP_ID"; | |
static NSString * const kApiKey = @"YOUR_API_KEY"; | |
@implementation CustomRootCAAPIClient | |
+ (instancetype)sharedInstance | |
{ | |
static CustomRootCAAPIClient *sharedInstance = nil; | |
static dispatch_once_t onceToken = 0; | |
dispatch_once(&onceToken, ^{ | |
sharedInstance = [[self alloc] initWithBaseURL:[NSURL URLWithString:kApiBaseURL]]; | |
}); | |
return sharedInstance; | |
} | |
- (instancetype)initWithBaseURL:(NSURL *)url | |
{ | |
self = [super initWithBaseURL:url]; | |
if (self) | |
{ | |
[self setRequestSerializer:[AFHTTPRequestSerializer serializer]]; | |
[self setResponseSerializer:[AFJSONResponseSerializer serializer]]; | |
[[self requestSerializer] setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; | |
[[self requestSerializer] setValue:kAPIApplicationId forHTTPHeaderField:@"X-Application-Id"]; | |
[[self requestSerializer] setValue:kApiKey forHTTPHeaderField:@"X-REST-API-Key"]; | |
// validate custom CA certificate | |
__unsafe_unretained typeof(self) weakSelf = self; | |
[self setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *credential) { | |
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; | |
// handle the authentication challenge | |
if ([weakSelf shouldTrustProtectionSpace:challenge.protectionSpace]) | |
{ | |
disposition = NSURLSessionAuthChallengeUseCredential; | |
} | |
return disposition; | |
}]; | |
} | |
return self; | |
} | |
/** | |
* Trust custom CA certificates | |
* | |
* Based on code samples by Jon Parise ( http://www.indelible.org ) and | |
* Dirk-Willem van Gulik ( http://www.webweaving.org ) | |
* | |
* Determines whether or not the remote host’s certificate matches the bundled | |
* certificate by establishing a chain of trust, "anchored" on the local certificate. | |
* | |
* Trusting only specified anchor certificate can be achieved by specified 'false' to | |
* 'SecTrustSetAnchorCertificatesOnly' method below. Default implementation trusts | |
* locally specified certificates AND iOS built-in root certificates. | |
* | |
* CRT certificate can be converted to DER using the following Terminal command: | |
* openssl x509 -in crt.crt -outform der -out crt.der | |
* | |
* @see https://github.com/AFNetworking/AFNetworking/blob/master/Tests/Tests/AFSecurityPolicyTests.m | |
* @see http://www.indelible.org/ink/trusted-ssl-certificates/ | |
* @see https://gist.github.com/dhoerl/2051599 | |
* @see https://github.com/dirkx/Security-Pinning-by-CA/blob/master/Security%20Pinning%20by%20CA/PinnedHTTPSConnection.m | |
*/ | |
- (BOOL)shouldTrustProtectionSpace:(NSURLProtectionSpace *)protectionSpace | |
{ | |
// load up the bundled root CA | |
NSString *certPath = [[NSBundle mainBundle] pathForResource:@"YOUR_CUSTOM_ROOT_CA" ofType:@"der"]; | |
NSAssert(certPath != nil, @"Specified certificate does not exist!"); | |
NSData *certData = [[NSData alloc] initWithContentsOfFile:certPath]; | |
CFDataRef certDataRef = (__bridge_retained CFDataRef)certData; | |
SecCertificateRef cert = SecCertificateCreateWithData(NULL, certDataRef); | |
NSAssert(cert != NULL, @"Failed to create certificate object. Is the certificate in DER format?"); | |
// establish a chain of trust anchored on our bundled certificate | |
CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)&cert, 1, NULL); | |
SecTrustRef serverTrust = protectionSpace.serverTrust; | |
OSStatus anchorCertificateStatus = SecTrustSetAnchorCertificates(serverTrust, certArrayRef); | |
NSAssert(anchorCertificateStatus == errSecSuccess, @"Failed to specify custom anchor certificate"); | |
// trust also built-in certificates besides the specified CA | |
OSStatus trustBuiltinCertificatesStatus = SecTrustSetAnchorCertificatesOnly(serverTrust, false); | |
NSAssert(trustBuiltinCertificatesStatus == errSecSuccess, @"Failed to reenable trusting built-in anchor certificates"); | |
// verify that trust | |
SecTrustResultType trustResult; | |
OSStatus evalStatus = SecTrustEvaluate(serverTrust, &trustResult); | |
NSAssert(evalStatus == errSecSuccess, @"Failed to evaluate certificate trust"); | |
// clean up | |
CFRelease(certArrayRef); | |
CFRelease(cert); | |
CFRelease(certDataRef); | |
// did our custom trust chain evaluate successfully | |
return (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm afraid that this implementation contains critical issue for iOS 11. We should change
CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)&cert, 1, NULL);
to
CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)&cert, 1, &kCFTypeArrayCallBacks);