Skip to content

Instantly share code, notes, and snippets.

@romantomjak
Last active August 25, 2017 10:04
Show Gist options
  • Save romantomjak/e450b8b391a4bf312966 to your computer and use it in GitHub Desktop.
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
//
// 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
//
// 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
@AnthonyBY
Copy link

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);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment