AFNetworking, as read in this article, is version 3.2.0.

The AFSecurityPolicy class is used to verify that the certificate for HTTPS requests is correct.

1. The interface

1.1. The enumeration

typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
    AFSSLPinningModeNone,
    AFSSLPinningModePublicKey,
    AFSSLPinningModeCertificate,
};
Copy the code

This enumeration defines the server certificate validation mode:

AFSSLPinningModeNone does not use a certificate authentication server

AFSSLPinningModePublicKey using the public key certificate authentication server

AFSSLPinningModeCertificate use certificate authentication server

1.2. The attribute

/** Server certificate authentication mode, default is not authentication */ @property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode; By default, AFNetworking searches for all. Cer certificate files in the project, but does not use a single certificate as the default. To create an AFSecurityPolicy object, load the certificate by calling the certificatesInBundle method. Then call policyWithPinningMode: withPinnedCertificates method to create object * / @ property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates; /** Whether to trust invalid or expired certificates, default is no */ @property (nonatomic, assign) BOOL allowInvalidCertificates; /** Whether to validate the domain name in the certificate. The default value is */ @property (nonatomic, assign) BOOL validatesDomainName;Copy the code

Method of 1.3.

/** use this method to find all certificates contained in the specified package */ + (NSSet <NSData *> *)certificatesInBundle:(NSBundle *)bundle; /** The default configuration includes disallowing invalid or expired certificates, validating domain names, not validating certificates, and public keys in certificates */ + (instanceType)defaultPolicy; */ + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode; /** specify the certificate validation mode and instance of the certificate chemical method */ + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet <NSData *> *)pinnedCertificates; EvaluateServerTrust :(SecTrustRef)serverTrustforDomain:(nullable NSString *)domain;
Copy the code

2. Implement

2.1. Private static methods

The method is to convert the key to NSData type

#if ! TARGET_OS_IOS && ! TARGET_OS_WATCH && ! TARGET_OS_TV
static NSData * AFSecKeyGetData(SecKeyRef key) {
    CFDataRef data = NULL;

    __Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out);

    return (__bridge_transfer NSData *)data;

_out:
    if (data) {
        CFRelease(data);
    }

    return nil;
}
#endif
Copy the code

This method is to determine whether two keys are equal

static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
    return [(__bridge id)key1 isEqual:(__bridge id)key2];
#else
    return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)];
#endif
}
Copy the code

This method is used to retrieve the public key from the certificate

static id AFPublicKeyForCertificate(NSData *certificate) { id allowedPublicKey = nil; SecCertificateRef allowedCertificate; SecPolicyRef policy = nil; SecTrustRef allowedTrust = nil; SecTrustResultType result; allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); __Require_Quiet(allowedCertificate ! = NULL, _out); policy = SecPolicyCreateBasicX509(); __Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out); __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); _out:if (allowedTrust) {
        CFRelease(allowedTrust);
    }

    if (policy) {
        CFRelease(policy);
    }

    if (allowedCertificate) {
        CFRelease(allowedCertificate);
    }

    return allowedPublicKey;
}
Copy the code

This method is to determine whether the server can be trusted

static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
    BOOL isValid = NO;
    SecTrustResultType result;
    __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);

    isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);

_out:
    return isValid;
}
Copy the code

This method is used to get all the certificates returned by the server

static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
    NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];

    for (CFIndex i = 0; i < certificateCount; i++) {
        SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
        [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
    }

    return [NSArray arrayWithArray:trustChain];
}
Copy the code

This method is used to obtain the public key in all certificates returned by the server

static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
    SecPolicyRef policy = SecPolicyCreateBasicX509();
    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
    NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
    for (CFIndex i = 0; i < certificateCount; i++) {
        SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);

        SecCertificateRef someCertificates[] = {certificate};
        CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);

        SecTrustRef trust;
        __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);

        SecTrustResultType result;
        __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);

        [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];

    _out:
        if (trust) {
            CFRelease(trust);
        }

        if (certificates) {
            CFRelease(certificates);
        }

        continue;
    }
    CFRelease(policy);

    return [NSArray arrayWithArray:trustChain];
}
Copy the code

There are two macros used frequently: __Require_Quiet and __Require_noErr_Quiet. Let’s look at their definitions:

#ifndef __Require_Quiet
	#define __Require_Quiet(assertion, exceptionLabel) \
	  do\ {\if( __builtin_expect(! (assertion), 0) ) \ { \ goto exceptionLabel; \} \}while(0)#endif
Copy the code

You can see that the __Require_Quiet macro is used to jump and execute the code after the tag when the assertion is false

#ifndef __Require_noErr_Quiet
	#define __Require_noErr_Quiet(errorCode, exceptionLabel) \
	  do\ {\if( __builtin_expect(0 ! = (errorCode), 0) ) \ { \ goto exceptionLabel; \} \}while(0)#endif
Copy the code

As you can see, the __Require_noErr_Quiet macro is used to jump and execute the code after the tag when the assertion is true

2.2 class extensions

/** Save server certificate authentication mode */ @property (readWrite, Nonatomic, assign) AFSSLPinningMode SSLPinningMode; /** Save the set of public keys in the certificate */ @property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys;Copy the code

2.3. To achieve

+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {// obtain all. Cer file paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; NSMutableSet *certificates = [NSMutableSet] // Walk through the file path and convert the corresponding files to NSDatasetWithCapacity:[paths count]];
    for (NSString *path inpaths) { NSData *certificateData = [NSData dataWithContentsOfFile:path]; [certificates addObject:certificateData]; } // Returns a collection of certificates converted to binaryreturn [NSSet setWithSet:certificates]; DefaultPinnedCertificates {} + (NSSet *) / / get the default certificate list static NSSet * _defaultPinnedCertificates = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSBundle *bundle = [NSBundle bundleForClass:[self class]]; _defaultPinnedCertificates = [self certificatesInBundle:bundle]; });return_defaultPinnedCertificates; } + (instanceType)defaultPolicy {// Instantiate the object by default AFSecurityPolicy *securityPolicy = [[self alloc] init]; securityPolicy.SSLPinningMode = AFSSLPinningModeNone;returnsecurityPolicy; } + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {// call the omnipowerful instantiation methodreturn[self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]]; } + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {// instantiate the object and set the property AFSecurityPolicy *securityPolicy = [[self alloc] init]; securityPolicy.SSLPinningMode = pinningMode; [securityPolicysetPinnedCertificates:pinnedCertificates];

    return securityPolicy;
}

- (instancetype)init {
    self = [super init];
    if(! self) {returnnil; } self.validatesDomainName = YES;return self;
}

- (void)setPinnedCertificates:(NSSet *)pinnedCertificates { _pinnedCertificates = pinnedCertificates; // If a certificate is set, fetch the public key in the certificate and save itif (self.pinnedCertificates) {
        NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
        for (NSData *certificate in self.pinnedCertificates) {
            id publicKey = AFPublicKeyForCertificate(certificate);
            if(! publicKey) {continue;
            }
            [mutablePinnedPublicKeys addObject:publicKey];
        }
        self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
    } else {
        self.pinnedPublicKeys = nil;
    }
}

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain: Domain nsstrings *) {/ / when using self-built certificate authentication Domain, must use AFSSLPinningModePublicKey or AFSSLPinningModeCertificate for validationif(domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/Overriding SSLChainValidationCorrectly.html // According to the docs, you should only trust your provided certsfor evaluation.
        //  Pinned certificates are added to the trust. Without pinned certificates,
        //  there is nothing to evaluate against.
        //
        //  From Apple Docs:
        //          "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors). // Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
        NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
        returnNO; } // Add a policy to verify domain names if necessary NSMutableArray *policies = [NSMutableArray array];if (self.validatesDomainName) {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else{ [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()]; ServerTrust SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); // If the authentication policy is no authenticationif(self.sslPinningMode == AFSSLPinningModeNone) {// If the trust certificate is invalid or expired, the certificate is directly authenticated, otherwise the certificate is returnedreturnself.allowInvalidCertificates || AFServerTrustIsValid(serverTrust); // If the validation policy is validation but failed and you do not trust invalid or expired certificates, return failed}else if(! AFServerTrustIsValid(serverTrust) && ! self.allowInvalidCertificates) {returnNO; } /** When the code goes here it means: 1. There is validation policy 2. */ switch (self.sslpinningMode) {// If there is no authentication policy, the certificate fails to pass the authenticationcase AFSSLPinningModeNone:
        default:
            returnNO; // If the validation policy is to validate the entire certificatecaseAFSSLPinningModeCertificate: {/ / iterates over to verify certificate NSMutableArray * pinnedCertificates = [NSMutableArray array];for (NSData *certificateData inself.pinnedCertificates) { [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; } SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); // Return if validation failsif(! AFServerTrustIsValid(serverTrust)) {returnNO; } // Obtain the chain after being validated,which *should* contain the pinned certificate in the last position (if it is the Root CA)
            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
            
            for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                    returnYES; }}returnNO; } // If the authentication policy is to verify the public key of the certificatecaseAFSSLPinningModePublicKey: {/ / get the server returns and comparison between the public and local public key as long as have a same authenticated NSUInteger trustedPublicKeyCount = 0; NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);for (id trustChainPublicKey in publicKeys) {
                for (id pinnedPublicKey in self.pinnedPublicKeys) {
                    if(AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { trustedPublicKeyCount += 1; }}}returntrustedPublicKeyCount > 0; }}return NO;
}
Copy the code

2.4. KVO

+ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys {
    return [NSSet setWithObject:@"pinnedCertificates"];
}
Copy the code

Add a dependency key to make the pinnedPublicKeys property dependent on the pinnedCertificates property so that observers of the pinnedPublicKeys can be notified when the Value of the pinnedCertificates property changes

2.5. Implementation of NSSecureCoding protocol

+ (BOOL)supportsSecureCoding {
    return YES;
}

- (instancetype)initWithCoder:(NSCoder *)decoder {

    self = [self init];
    if(! self) {return nil;
    }

    self.SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(SSLPinningMode))] unsignedIntegerValue];
    self.allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
    self.validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector(@selector(validatesDomainName))];
    self.pinnedCertificates = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(pinnedCertificates))];

    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:[NSNumber numberWithUnsignedInteger:self.SSLPinningMode] forKey:NSStringFromSelector(@selector(SSLPinningMode))];
    [coder encodeBool:self.allowInvalidCertificates forKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
    [coder encodeBool:self.validatesDomainName forKey:NSStringFromSelector(@selector(validatesDomainName))];
    [coder encodeObject:self.pinnedCertificates forKey:NSStringFromSelector(@selector(pinnedCertificates))];
}
Copy the code

About NSSecureCoding AFNetworking source reading (2), from the AFURLRequestSerialization “4.1.2.4.7 NSSecureCoding implementation methods of agreement” in has been explained, will not repeat

2.6. Implementation of NSCopying protocol

- (instancetype)copyWithZone:(NSZone *)zone {
    AFSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init];
    securityPolicy.SSLPinningMode = self.SSLPinningMode;
    securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates;
    securityPolicy.validatesDomainName = self.validatesDomainName;
    securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone];

    return securityPolicy;
}
Copy the code

3. Summary

Since I don’t know much about HTTP and SSL, I have only read the general features. To learn more about HTTP and SSL, learn more about HTTP and SSL.

AFNetworking

AFNetworking (A) — Start using it

Source: reading AFNetworking (2) — AFURLRequestSerialization

Source: reading AFNetworking (3) — AFURLResponseSerialization

AFNetworking (4) — AFSecurityPolicy

Source: reading AFNetworking (5) — AFNetworkReachabilityManager

AFNetworking (6) — Afurlssession Manager

AFNetworking (7) — Afhttpssession Manager

AFNetworking (8) – AFAutoPurgingImageCache

AFNetworking (9) — AFImageDownloader

The source code to read: AFNetworking (10) — AFNetworkActivityIndicatorManager

AFNetworking — UIActivityIndicatorView+AFNetworking

AFNetworking (12) — UIButton+AFNetworking

AFNetworking (13) — UIImageView+AFNetworking

UIProgressView+AFNetworking

AFNetworking — UIRefreshControl+AFNetworking

UIWebView+AFNetworking