-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pluggable, more flexible, security policies. (#429)
Extract @FredericJacobs' CertificateVerifier concept with @nlutsenko's SRSecurityOptions into a pluggable SRSecurityPolicy model This retains existing SSL configuration code paths, while allowing users more flexibility to specify their own security policy. If you are alread using AFNetworking and an `AFSecurityPolicy`, it's intended that you can share domain trust logic by delegating `SRSecurityPolicy evaluateTrust:ForDomain` to your AFSecurityPolicy instance. Inspired by original "Require TLS 1.2 & enable pinning" pull request by Frederic Jacobs (@FredericJacobs) at: https://github.com/facebook/SocketRocket/pull/274/files
- Loading branch information
1 parent
b4e7932
commit 8096fef
Showing
10 changed files
with
329 additions
and
203 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// | ||
// Copyright (c) 2016-present, Facebook, Inc. | ||
// All rights reserved. | ||
// | ||
// This source code is licensed under the BSD-style license found in the | ||
// LICENSE file in the root directory of this source tree. An additional grant | ||
// of patent rights can be found in the PATENTS file in the same directory. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
#import "SRSecurityPolicy.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface SRPinningSecurityPolicy : SRSecurityPolicy | ||
|
||
- (instancetype)initWithCertificates:(NSArray *)pinnedCertificates; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// | ||
// Copyright (c) 2016-present, Facebook, Inc. | ||
// All rights reserved. | ||
// | ||
// This source code is licensed under the BSD-style license found in the | ||
// LICENSE file in the root directory of this source tree. An additional grant | ||
// of patent rights can be found in the PATENTS file in the same directory. | ||
// | ||
|
||
#import "SRPinningSecurityPolicy.h" | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
#import "SRLog.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface SRPinningSecurityPolicy () | ||
|
||
@property (nonatomic, copy, readonly) NSArray *pinnedCertificates; | ||
|
||
@end | ||
|
||
@implementation SRPinningSecurityPolicy | ||
|
||
- (instancetype)initWithCertificates:(NSArray *)pinnedCertificates | ||
{ | ||
// Do not validate certificate chain since we're pinning to specific certificates. | ||
self = [super initWithCertificateChainValidationEnabled:NO]; | ||
if (!self) { return self; } | ||
|
||
if (pinnedCertificates.count == 0) { | ||
@throw [NSException exceptionWithName:@"Creating security policy failed." | ||
reason:@"Must specify at least one certificate when creating a pinning policy." | ||
userInfo:nil]; | ||
} | ||
_pinnedCertificates = [pinnedCertificates copy]; | ||
|
||
return self; | ||
} | ||
|
||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain | ||
{ | ||
SRDebugLog(@"Pinned cert count: %d", self.pinnedCertificates.count); | ||
NSUInteger requiredCertCount = self.pinnedCertificates.count; | ||
|
||
NSUInteger validatedCertCount = 0; | ||
CFIndex serverCertCount = SecTrustGetCertificateCount(serverTrust); | ||
for (CFIndex i = 0; i < serverCertCount; i++) { | ||
SecCertificateRef cert = SecTrustGetCertificateAtIndex(serverTrust, i); | ||
NSData *data = CFBridgingRelease(SecCertificateCopyData(cert)); | ||
for (id ref in self.pinnedCertificates) { | ||
SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; | ||
// TODO: (nlutsenko) Add caching, so we don't copy the data for every pinned cert all the time. | ||
NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert)); | ||
if ([trustedCertData isEqualToData:data]) { | ||
validatedCertCount++; | ||
break; | ||
} | ||
} | ||
} | ||
return (requiredCertCount == validatedCertCount); | ||
} | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// | ||
// Copyright (c) 2016-present, Facebook, Inc. | ||
// All rights reserved. | ||
// | ||
// This source code is licensed under the BSD-style license found in the | ||
// LICENSE file in the root directory of this source tree. An additional grant | ||
// of patent rights can be found in the PATENTS file in the same directory. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
#import <Security/Security.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface SRSecurityPolicy : NSObject | ||
|
||
/** | ||
A default `SRSecurityPolicy` implementation specifies socket security and | ||
validates the certificate chain. | ||
Use a subclass of `SRSecurityPolicy` for more fine grained customization. | ||
*/ | ||
+ (instancetype)defaultPolicy; | ||
|
||
/** | ||
Specifies socket security and provider certificate pinning, disregarding certificate | ||
chain validation. | ||
@param pinnedCertificates Array of `SecCertificateRef` SSL certificates to use for validation. | ||
*/ | ||
+ (instancetype)pinnningPolicyWithCertificates:(NSArray *)pinnedCertificates; | ||
|
||
/** | ||
Specifies socket security and optional certificate chain validation. | ||
@param enabled Whether or not to validate the SSL certificate chain. If you | ||
are considering using this method because your certificate was not issued by a | ||
recognized certificate authority, consider using `pinningPolicyWithCertificates` instead. | ||
*/ | ||
- (instancetype)initWithCertificateChainValidationEnabled:(BOOL)enabled NS_DESIGNATED_INITIALIZER; | ||
|
||
/** | ||
Updates all the security options for input and output streams, for example you | ||
can set your socket security level here. | ||
@param stream Stream to update the options in. | ||
*/ | ||
- (void)updateSecurityOptionsInStream:(NSStream *)stream; | ||
|
||
/** | ||
Whether or not the specified server trust should be accepted, based on the security policy. | ||
This method should be used when responding to an authentication challenge from | ||
a server. In the default implemenation, no further validation is done here, but | ||
you're free to override it in a subclass. See `SRPinningSecurityPolicy.h` for | ||
an example. | ||
@param serverTrust The X.509 certificate trust of the server. | ||
@param domain The domain of serverTrust. | ||
@return Whether or not to trust the server. | ||
*/ | ||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// | ||
// Copyright (c) 2016-present, Facebook, Inc. | ||
// All rights reserved. | ||
// | ||
// This source code is licensed under the BSD-style license found in the | ||
// LICENSE file in the root directory of this source tree. An additional grant | ||
// of patent rights can be found in the PATENTS file in the same directory. | ||
// | ||
|
||
#import "SRSecurityPolicy.h" | ||
#import "SRPinningSecurityPolicy.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface SRSecurityPolicy () | ||
|
||
@property (nonatomic, assign, readonly) BOOL certificateChainValidationEnabled; | ||
|
||
@end | ||
|
||
@implementation SRSecurityPolicy | ||
|
||
+ (instancetype)defaultPolicy | ||
{ | ||
return [self new]; | ||
} | ||
|
||
+ (instancetype)pinnningPolicyWithCertificates:(NSArray *)pinnedCertificates | ||
{ | ||
return [[SRPinningSecurityPolicy alloc] initWithCertificates:pinnedCertificates]; | ||
} | ||
|
||
- (instancetype)initWithCertificateChainValidationEnabled:(BOOL)enabled | ||
{ | ||
self = [super init]; | ||
if (!self) { return self; } | ||
|
||
_certificateChainValidationEnabled = enabled; | ||
|
||
return self; | ||
} | ||
|
||
- (instancetype)init | ||
{ | ||
return [self initWithCertificateChainValidationEnabled:YES]; | ||
} | ||
|
||
- (void)updateSecurityOptionsInStream:(NSStream *)stream | ||
{ | ||
// Enforce TLS 1.2 | ||
[stream setProperty:(__bridge id)CFSTR("kCFStreamSocketSecurityLevelTLSv1_2") forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; | ||
|
||
// Validate certificate chain for this stream if enabled. | ||
NSDictionary<NSString *, id> *sslOptions = @{ (__bridge NSString *)kCFStreamSSLValidatesCertificateChain : @(self.certificateChainValidationEnabled) }; | ||
[stream setProperty:sslOptions forKey:(__bridge NSString *)kCFStreamPropertySSLSettings]; | ||
} | ||
|
||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain | ||
{ | ||
// No further evaluation happens in the default policy. | ||
return YES; | ||
} | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
Oops, something went wrong.