Skip to content

Instantly share code, notes, and snippets.

@vhyrbi
Last active February 13, 2022 10:59
Show Gist options
  • Save vhyrbi/35c65c88252684bb4f50 to your computer and use it in GitHub Desktop.
Save vhyrbi/35c65c88252684bb4f50 to your computer and use it in GitHub Desktop.
iOS - How to add HTTPS support for one's own Certificate Authority
#import <Security/Security.h>
#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
@interface SSLHelper : NSObject
+ (SSLHelper *)sharedInstance;
- (void) addCertToKeychain:(NSData*)certInDer;
- (BOOL) canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
- (BOOL) manageAuthorizationForChallenge:(NSURLAuthenticationChallenge*)challenge;
@end
/*
From:
- https://developer.apple.com/library/ios/technotes/tn2232
- http://stackoverflow.com/questions/5323686/ios-pre-install-ssl-certificate-in-keychain-programmatically/9941559#9941559
- http://stackoverflow.com/questions/11608847/always-exc-bad-access-on-sectrustevaluate
# New server private key
openssl genrsa 2048 -out server-key.pem
# Create a certificate sign request
openssl req -new -key server-key.pem -out certificate.csr
# Autosign certificate sign request with own server private key
# The result is an autosign certificate that can also be a Certificate Authority
openssl x509 -req -days 65537 -in certificate.csr -signkey server-key.pem -out certificate.crt
# Generate certificate in format compatible with iOS
openssl x509 -in certificate.crt -inform PEM -out certificate.der -outform DER
Usage example, with certificate.der file:
// Load certificate as new Certificate Authority
NSBundle * bundle = [NSBundle bundleForClass:[self class]];
NSData *trustedCertDerData = [NSData dataWithContentsOfFile:[bundle pathForResource:@"certificate" ofType:@"der"]];
[[SSLHelper sharedInstance] addCertToKeychain:trustedCertDerData];
// For NSURLConnection, add hooks to delegate NSURLConnectionDelegate on:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [[SSLHelper sharedInstance] canAuthenticateAgainstProtectionSpace:protectionSpace];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
[[SSLHelper sharedInstance] manageAuthorizationForChallenge:challenge];
}
*/
@interface SSLHelper () {
NSMutableArray * _certRefsArray;
}
- (BOOL) evaluateWithLocalCertificatesForTrust:(SecTrustRef)trust;
@end
@implementation SSLHelper
+ (SSLHelper *)sharedInstance
{
static SSLHelper * sInstance = nil;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{
sInstance = [[self alloc] init];
});
return sInstance;
}
- (id)init
{
self = [super init];
if (self) {
_certRefsArray = [NSMutableArray new];
}
return self;
}
- (void)dealloc
{
for (id cert in _certRefsArray) {
CFRelease((__bridge CFTypeRef)(cert));
}
}
- (void) addCertToKeychain:(NSData*)certInDer
{
CFDataRef certDataRef = (__bridge CFDataRef)(certInDer);
SecCertificateRef caCertRef = SecCertificateCreateWithData(NULL, certDataRef);
assert(caCertRef);
[_certRefsArray addObject:(__bridge id)(caCertRef)];
}
- (BOOL)canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (BOOL) evaluateWithLocalCertificatesForTrust:(SecTrustRef)trust
{
OSStatus err;
SecTrustResultType trustResult;
BOOL trusted;
err = SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)_certRefsArray);
if (err == noErr) {
err = SecTrustEvaluate(trust, &trustResult);
trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
}
else {
trusted = false;
}
return trusted;
}
- (BOOL) manageAuthorizationForChallenge:(NSURLAuthenticationChallenge*)challenge
{
// Check if challenge is really for SSL
if (! [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
return NO;
}
OSStatus err;
NSURLProtectionSpace * protectionSpace;
SecTrustRef trust;
SecTrustResultType trustResult;
BOOL trusted;
protectionSpace = [challenge protectionSpace];
assert(protectionSpace != nil);
// Default trust evaluation
trust = [protectionSpace serverTrust];
assert(trust != NULL);
err = SecTrustEvaluate(trust, &trustResult);
trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
// If that fails, apply our certificates as anchors and see if that helps
// Note: the certificate will then stay as an anchor certificates, so the following code is applied only once
if ( ! trusted ) {
NSLog(@"Certificate not trusted, trying our CA");
trusted = [[SSLHelper sharedInstance] evaluateWithLocalCertificatesForTrust:trust];
}
if(trusted) {
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
return YES;
}
return NO;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment