AF Security Policy of AFNet Working Source

Posted by toddg on Sat, 06 Jul 2019 01:03:58 +0200

1 HTTPS and SSL/TSL

Secure Sockets Layer (Secure Sockets Layer), because the HTTP protocol used on the Internet is plain text, there are many shortcomings, such as transmission content will be peeped and tampered with. The role of the SSL protocol is to encrypt the network connection at the transport layer.

By 1999, because of its wide application, SSL had become the de facto standard on the Internet. IETF standardized SSL that year. The standardized name was changed to TLS (Transport Layer Security, Transport Layer Security). SSL and TLS can be regarded as different stages of the same thing.

Simply put, HTTPS = HTTP + SSL/TLS, that is, HTTP over SSL or HTTP over TLS, which is the origin of S.

The similarities and differences between HTTPS and HTTP: HTTP and HTTPS use totally different connection modes and ports, the former is 80, the latter is 443. HTTP connection is simple and stateless; HTTPS protocol is a network protocol constructed by SSL+HTTP protocol, which can be used for encryption, transmission and authentication, and is safer than HTTP protocol.

2 HTTPS handshake

Client Hello

First, the client (usually the browser) sends a request for encrypted communication to the server, which is called a ClientHello request. In this step, the client mainly provides the following information to the server.

  • (1) Supported protocol versions, such as TLS version 1.0.

  • (2) The random number generated by a client is later used to generate the "conversation key".

  • (3) Supported encryption methods, such as RSA public key encryption.

  • (4) Supported compression methods.

SeverHello

When the server receives a client request, it sends a response to the client, which is called SeverHello. The server's response contains the following.

  • (1) Verify the version of the encrypted communication protocol used, such as TLS version 1.0. If the browser does not match the version supported by the server, the server closes the encrypted communication.

  • (2) A random number generated by a server, which is later used to generate a "conversation key".

  • (3) Confirm the encryption methods used, such as RSA public key encryption.

  • (4) Server certificate.

3 Client Response

After the client receives the response from the server, the server certificate is verified first. If the certificate is not issued by a trusted institution, or the domain name in the certificate is inconsistent with the actual domain name, or the certificate has expired, a warning will be displayed to the visitor to decide whether to continue the communication. If there is no problem with the certificate, the client will extract the server's public key from the certificate. Then, send the following three messages to the server.

  • (1) A random number. The random number is encrypted with the server public key to prevent eavesdropping.

  • (2) Coding change notification, indicating that subsequent information will be sent with the agreed encryption method and key.

  • (3) The notification of the end of the handshake indicates that the handshake phase of the client has ended. This item is also the hash value of all the previous content sent for server verification.

The random number of the first item above is the third random number appearing in the whole handshake stage, also known as "pre-master key". With it, the client and the server have three random numbers at the same time. Then they use the pre-agreed encryption method to generate the same "session key" for the session.

4 Server's Final Response

After the server receives the client's third random number pre-master key, it calculates the "session key" used to generate the session. Then, send the following information to the client finally.

  • (1) Coding change notification, indicating that subsequent information will be sent using the agreed encryption method and key.

  • (2) The server handshake end notification indicates that the handshake stage of the server has ended. This item is also the hash value of all the previous content sent for client verification.

So far, the whole handshake phase is over. Next, when the client and the server enter the encrypted communication, they use the ordinary HTTP protocol completely, but encrypt the content with the session key.

3 Digital Certificate

In the second step of the handshake stage above, the certificate given by the server to the client is a digital certificate. The certificate contains information such as public key, which is usually sent by the server to the client. The recipient determines whether the certificate is credible by verifying whether the certificate is issued by a trusted CA or by comparing it with the local certificate. For two-way authentication, both the server and the client need to send digital certificates to each other for verification.

A digital certificate is an electronic document that contains the information of the holder, the public key and the digital signature that proves the validity of the certificate. Digital certificate and related public key management and verification technologies constitute the PKI (Public Key Infrastructure) specification system. Generally speaking, digital certificates are issued and managed by Certificate authority (CA) and bear the responsibility of verifying the validity of public key in PKI system. There are many types of digital certificates, while HTTPS uses SSL certificates.

How to verify that a digital certificate is issued by a CA, not a forged third party? Before answering this question, we need to understand the organizational structure of CA. Firstly, the top level of CA organization structure is root CA, which can be authorized to multiple secondary CAs, while secondary CA can authorize multiple tertiary CAs, so the organization structure of CA is a tree structure. For the SSL certificate market, it is mainly divided up by Symantec (VeriSign and GeoTrust), Comodo SSL, Go Daddy and GlobalSign. After understanding the organization structure of CA, let's look at the process of issuing digital certificates.

CA, the issuing agency of digital certificates, checks the applicant's information after receiving it and confirms its authenticity and validity, and then produces a document that meets the X.509 standard. Certificate contents, including holder information and public key, are provided by the applicant, while digital signature is obtained by CA after hash encryption of certificate contents. This digital signature is to verify whether the certificate has data issued by trusted CA.

After receiving a digital certificate Cer1, the recipient makes a Hash to the content of the certificate until H1; then finds the public key in the digital certificate of CA1, which is the institution that issued the certificate, decrypts the digital signature on the certificate, and obtains the H ash digest H2 signed by Cer1; comparing H1 and H2, if they are equal, the certificate is not tampered with. But at this time, we still don't know whether CA is legal. We can see the digital certificate of CA organization in the picture above. This certificate is public and can be obtained by everyone. The digital signature in this certificate is generated by the previous level, so it can be verified recursively until the root CA. The root CA is self-verifying, that is, its digital signature is generated by its own private key. The legitimate root CA is added to the authoritative trust CA list by the browser and operating system, which completes the final verification. Therefore, we must protect the root CA trust list in our environment (browser/operating system). Trusting the root CA means trusting all the certificates issued by all the sub-CAs under the root CA. Do not add the root CA certificates casually.

4 SSL Pinning

It can be understood as certificate binding, which means that the client directly saves the certificate of the server, and directly compares the two certificates returned by the server and saved by the client when establishing https connection, which indicates that the certificate is true and no longer searches for verification in the trust certificate authority of the system. This applies to non-browser applications, because browsers deal with many unknown servers, and can not save the certificates of each server locally, but CS architecture, like mobile APP, knows beforehand the server to communicate, can store the certificates of this server directly in the client for verification.

Why does direct comparison guarantee that certificates are OK? If the middleman takes out the certificate from the client and pretends that the server communicates with other clients, can't the certificate it sends to the client be validated? Certainly it can be verified, but the subsequent process can not go on, because the client will encrypt with the public key in the certificate next step, the intermediary will not be able to solve the content without the private key of the certificate, so it can not intercept the data. The private key of the certificate is only owned by the real server, and the intermediary forges the certificate mainly by the public key.

Why use SSL Pinning? Is normal verification not enough? If the certificate on the server side is issued by a trusted CA institution, verification is not a problem, but certificate issuance by CA institution is more expensive. Small enterprises or individual users may choose to issue certificates themselves, so it is impossible to verify the authenticity of the certificate through the list of trusted CA institutions in the system, so SSL Ping is needed. Sample way to verify.

5 iOS HTTP request

Next, I will implement self-signed certificate (12306), SSL trust certificate (baidu), system certificate (Apple) to see their differences. Baidu and 12306 certificates have been downloaded to my project, and you can go to Demo to see the implementation process.

1 self-signed certificate

We specify the security policy authentication attribute manually. It is implemented by 12306 certificate.

//Self-built Certificate Authentication
- (IBAction)buttion1:(id)sender {
    NSURL *url = [NSURL URLWithString:@"https://kyfw.12306.cn/otn/leftTicket/init"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
   // [request setValue:@"text/html" forHTTPHeaderField:@"Accept"];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    //Specify a security policy
    manager.securityPolicy = [self ticketSecurityPolicy];
    //Specify the return data type, default is AFJSONResponseSerializer type, hesitate here is not JSON type return data, so you need to specify the return type manually.
    AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
    responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];
    manager.responseSerializer = responseSerializer;
    NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
        NSLog(@"%@-----%@",[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding],error);
    }];
    [dataTask resume];

}
/**
 12306 Certificate of Certification. His Certificate of Certification is self-signed.

 @return Returns the specified authentication policy
 */
-(AFSecurityPolicy*)ticketSecurityPolicy {
    // / Import Certificate
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"12306" ofType:@"cer"];//Certificate path
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    NSSet *set = [NSSet setWithObject:certData];
    
    AFSecurityPolicy *securityPolicy;
    if (true) {
        securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:set];
    }else{
        // AFSSL Pinning Model Certificate uses certificate authentication mode. The following method defaults to all certificates in the project
        securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    }
    // Whether allowInvalid Certificates allow invalid certificates (i.e. self-built certificates), default to NO
    // If you need to validate your build-in certificate, you need to set it to YES
    securityPolicy.allowInvalidCertificates = YES;
    
    //Whether validates DomainName needs to validate the domain name is YES by default;
    //If the domain name of the certificate does not match the domain name you requested, you need to set this to NO. If NO is set, the server can also establish a connection using certificates issued by other trusted agencies. This is very dangerous. It is recommended to open it.
    //Setting it to NO is mainly used in this case: the client requests a subdomain name and the certificate has another domain name. Because the domain name on the SSL certificate is independent, if the domain name registered on the certificate is www.google.com, then mail.google.com can not be verified; of course, the domain name of the wildcard can be registered with money*.google.com, but this is more expensive.
    //If set to NO, it is recommended to add the verification logic of the corresponding domain name.
    securityPolicy.validatesDomainName = NO;
    
    return securityPolicy;
}

2 SSL Trust Certificate

We specify the security policy authentication attribute manually. Through Baidu Certificate to achieve.

//Certificate Authentication
- (IBAction)button2:(id)sender {
    NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    //[request setValue:@"text/html" forHTTPHeaderField:@"Accept"];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    //Specify a security policy
    manager.securityPolicy = [self baiduSecurityPolicy];
    //Specify the return data type, default is AFJSONResponseSerializer type, hesitate here is not JSON type return data, so you need to specify the return type manually.
    AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
    responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];
    manager.responseSerializer = responseSerializer;
    NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
        NSLog(@"%@-----%@",[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding],error);
    }];
    [dataTask resume];
}

/**
Baidu's certificate, his certificate is paid for, that is, not self-signed certificate. This certificate, if we want to specify it manually, pinmode can only be `AFSSL Pinning ModeNone'.`
 
 @return Returns the specified authentication policy
 */
-(AFSecurityPolicy*)baiduSecurityPolicy {
    // / Import Certificate
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"baidu" ofType:@"cer"];//Certificate path
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    NSSet *set = [NSSet setWithObject:certData];
    
    AFSecurityPolicy *securityPolicy;
    if (true) {
        //Here we can only use AFSSL Pinning ModeNone to succeed, and the certificates of Baidu already exist in the certificate list of my system.
        securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone withPinnedCertificates:set];
    }else{
        // AFSSL Pinning Model Certificate uses certificate authentication mode. The following method defaults to all certificates in the project
        securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
    }
    // Whether allowInvalid Certificates allow invalid certificates (i.e. self-built certificates), default to NO
    // If you need to validate your build-in certificate, you need to set it to YES
    securityPolicy.allowInvalidCertificates = NO;
    
    //Whether validates DomainName needs to validate the domain name is YES by default;
    //If the domain name of the certificate does not match the domain name you requested, you need to set this to NO. If NO is set, the server can also establish a connection using certificates issued by other trusted agencies. This is very dangerous. It is recommended to open it.
    //Setting it to NO is mainly used in this case: the client requests a subdomain name and the certificate has another domain name. Because the domain name on the SSL certificate is independent, if the domain name registered on the certificate is www.google.com, then mail.google.com can not be verified; of course, the domain name of the wildcard can be registered with money*.google.com, but this is more expensive.
    //If set to NO, it is recommended to add the verification logic of the corresponding domain name.
    securityPolicy.validatesDomainName = YES;
    
    return securityPolicy;
}

3 SSL Certificate AFN Default Processing

We do not do any additional processing here, but use AFN's default certificate processing mechanism directly. It is implemented by default through the security policy of AFURLSession Manager. It will be compared with the existing system to verify the certificate.

//System Certificate Authentication
- (IBAction)button3:(id)sender {
    NSURL *url = [NSURL URLWithString:@"https://www.apple.com/"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    //Specify the return data type, default is AFJSONResponseSerializer type, hesitate here is not JSON type return data, so you need to specify the return type manually.
    AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
    responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];
    manager.responseSerializer = responseSerializer;
    NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
        NSLog(@"%@-----%@",[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding],error);
    }];
    [dataTask resume];
}

6 AFSecurity Policy Source Parsing

AFSecurity Policy is divided into three verification modes

  • AFSSLPinningModeNone:

    This pattern means that instead of using SSL pinning, the certificate returned by the server is validated in the list of trusted institutions of the system just like the browser. If the certificate is issued by the trust agency, it will pass. If the certificate is generated by the server, it will not pass here.
  • AFSSLPinningModeCertificate:

    This mode indicates that certificates are validated by certificate binding, which requires the client to keep a copy of the certificate from the server. There are two steps to validate the domain name/validity of the certificate. The second step is to compare whether the certificate returned by the server is consistent with the certificate returned by the client. It's not clear how the first step of validation works. The code calls SecTrust Evaluate just as it does in the system trust agency list, except that the list here is replaced by the list of certificates saved by the client. To verify this, should the certification authority root certificate of the server certificate also be placed in the client?
  • AFSSLPinningModePublicKey:

    This mode is also validated by certificate binding. The client needs a copy of the certificate of the server, but only the public key in the certificate is validated, and the validity of the certificate is not validated. As long as the public key is correct, the communication will not be eavesdropped, because the intermediary has no private key and can not unlock the data encrypted by the public key.
    

SecTrustRef

This is a trust object that needs to be validated, including certificates to be validated and supporting validation methods.

SecTrustResultType

Represents the verification results. Where kSecTrustResultProceed indicates server Trust validation is successful, and the validation is approved by the user (for example, select always trust in the alert box that pops up whether to trust). KSecTrust Result Unspecified indicates that server Trust was validated successfully and that the certificate was secretly trusted, but the user did not explicitly decide to trust the certificate. Either way, serverTrust validation can be considered successful.

SecTrustEvaluate

Certificate validation function recursively from leaf node certificate to root certificate validation within the function. It is necessary to verify the validity of the certificate itself (verify signature integrity, verify certificate validity, etc.); verify the validity of the certificate issuer (find the certificate of the issuer and check its validity, this process is recursive). The recursive termination condition is that the anchor certificate is encountered in the certificate verification process (anchor certificate: embedded into the operation). The root certificate in the system is the self-signed certificate issued by the authoritative certificate authority.

The source details of AFSecurity Policy are as follows:

/**
 Certificate authentication type

 - AFSSLPinningModeNone: Certificates are not validated using `pinned certificates'.
 - AFSSLPinningModePublicKey: Use `pinned certificates'to verify the certificate's public key
 - AFSSLPinningModeCertificate: Use `pinned certificates'to verify the entire certificate
 */
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
    AFSSLPinningModeNone,
    AFSSLPinningModePublicKey,
    AFSSLPinningModeCertificate,
};

/**
 Gets the public key of the specified certificate

 @param certificate Certificate data
 @return public key
 */
static id AFPublicKeyForCertificate(NSData *certificate) {
    id allowedPublicKey = nil;
    SecCertificateRef allowedCertificate;
    SecPolicyRef policy = nil;
    SecTrustRef allowedTrust = nil;
    SecTrustResultType result;
    //Acquire Certificate Object
    allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
    __Require_Quiet(allowedCertificate != NULL, _out);
    //Acquisition of X.509 Authentication Strategy
    policy = SecPolicyCreateBasicX509();
    //Get the value of the allowedTrust object
    __Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
    __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
    //Get the corresponding public key according to allowedTrust
    allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
//C++ gumpto jump, after the current operation error, jump directly into _out execution
_out:
    if (allowedTrust) {
        CFRelease(allowedTrust);
    }
    if (policy) {
        CFRelease(policy);
    }
    if (allowedCertificate) {
        CFRelease(allowedCertificate);
    }
    //Return the public key
    return allowedPublicKey;
}

/**
 Verify that the SecTrustRef object is trusted and legitimate under the specified certificate and authentication policies.

 @param serverTrust SecTrustRef object
 @return Result
 */
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
    BOOL isValid = NO;
    SecTrustResultType result;
    //Get the authentication result of server Trust and call `SecTrust Evaluate'to indicate that authentication is compared through system certificates
    __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
    isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);

_out:
    return isValid;
}

/**
 Acquisition of Certificate Chain Based on `SerrTrust'

 @param serverTrust serverTrust object
 @return Authentication Certificate Chain
 */
static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
    //Acquiring the Overall Level of the Authentication Chain
    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
    NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
    //Get each level of authentication chain and store the acquired certificate data in an array
    for (CFIndex i = 0; i < certificateCount; i++) {
        SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
        [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
    }
    //Returns an array of certificate chains
    return [NSArray arrayWithArray:trustChain];
}

/**
 Get the public key array of the authentication chain of the server Trust object

 @param serverTrust serverTrust object
 @return Public key array
 */
static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
    //Security Policy of X.509 Standard
    SecPolicyRef policy = SecPolicyCreateBasicX509();
    //Get the number of certificates in the certificate chain
    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;
        //Create a new SecTrustRef object through a certificate and authentication policy
        __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
        
        SecTrustResultType result;
        //Verify that the SecTrustRef object is successful
        __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
        //Add the public key corresponding to SecTrustRef to the array
        [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];

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

        if (certificates) {
            CFRelease(certificates);
        }

        continue;
    }
    CFRelease(policy);

    return [NSArray arrayWithArray:trustChain];
}

#pragma mark -

@interface AFSecurityPolicy()
//Authentication strategy
@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
//Public key set
@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys;
@end

@implementation AFSecurityPolicy
/**
 Obtain all certificates from MainBundle
 
 @param bundle Returns the collection of certificates contained in the bundle. If AFNetworking uses static libraries, we have to load certificates in this way. And the authentication type is specified by the `policy With Pinning Mode: with Pinned Certificates'method.
 @return Return the certificate in bundle
 */
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
    //Get all. cer certificates in the project
    NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
    NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
    for (NSString *path in paths) {
        //Get the NSData corresponding to the certificate and join the collection
        NSData *certificateData = [NSData dataWithContentsOfFile:path];
        [certificates addObject:certificateData];
    }
    //Return certificate set
    return [NSSet setWithSet:certificates];
}
/**
 Returns the collection of certificates where the bundle of the current class resides
 
 @return Certificate Set
 */
+ (NSSet *)defaultPinnedCertificates {
    static NSSet *_defaultPinnedCertificates = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //Get the bundle where the current class is located
        NSBundle *bundle = [NSBundle bundleForClass:[self class]];
        _defaultPinnedCertificates = [self certificatesInBundle:bundle];
    });

    return _defaultPinnedCertificates;
}
/**
 Returns the default security authentication policy, where the certificate of the authentication system is validated. This policy does not allow illegal certificates, authentication of host names, authentication of certificate contents and public keys
 
 @return Return Authentication Policy
 */
+ (instancetype)defaultPolicy {
    AFSecurityPolicy *securityPolicy = [[self alloc] init];
    securityPolicy.SSLPinningMode = AFSSLPinningModeNone;

    return securityPolicy;
}

/**
 Initialize an `AFSecurity Policy'object based on the specified authentication policy and the default certificate list

 @param pinningMode Authentication strategy
 @return `AFSecurityPolicy`object
 */
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
    return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
}

/**
 Initialize an `AFSecurity Policy'object by developing an authentication strategy `pinning mode' and a certificate set `pinned Certificates'.

 @param pinningMode Authentication Model
 @param pinnedCertificates Certificate Set
 @return AFSecurityPolicy object
 */
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
    AFSecurityPolicy *securityPolicy = [[self alloc] init];
    securityPolicy.SSLPinningMode = pinningMode;
    //Set `pinnedCertificates'and `pinnedPublicKeys' attributes, corresponding to the certificate set and the public key set, respectively
    [securityPolicy setPinnedCertificates:pinnedCertificates];
    //Return the `AFSecurity Policy'with successful initialization`
    return securityPolicy;
}

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }
    //The default is to authenticate the host name
    self.validatesDomainName = YES;
    
    return self;
}

/**
The corresponding set of public keys is obtained by combining the specified certificates. Then assign the `pinned PublicKeys'attribute
 @param pinnedCertificates Certificate Set
 */
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
    _pinnedCertificates = pinnedCertificates;

    if (self.pinnedCertificates) {
        NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
        //Iterate each certificate
        for (NSData *certificate in self.pinnedCertificates) {
            //Get the public key corresponding to the certificate
            id publicKey = AFPublicKeyForCertificate(certificate);
            if (!publicKey) {
                continue;
            }
            [mutablePinnedPublicKeys addObject:publicKey];
        }
        //Assignment to corresponding attributes
        self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
    } else {
        self.pinnedPublicKeys = nil;
    }
}

#pragma mark -
/**
 Specify authentication policies for serverTrust objects, including authentication of host names if domain is not nil. This method must be called when `authentication challenge'is returned.
 SecTrustRef It can be understood as the object that bridges the certificate and authentication policy, and it associates the specified certificate and authentication policy.
 
 @param serverTrust X.509 Standard Certificate Data for Servers
 @param domain The host name of the authentication server. If it is nil, the host name will not be authenticated.
 @return serverTrust Is it certified?
 */
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain
{
    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
        // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
        //  According to the docs, you should only trust your provided certs for 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.");
        return NO;
    }

    NSMutableArray *policies = [NSMutableArray array];
    if (self.validatesDomainName) {
        //Authentication strategy for using host names that need to be authenticated
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
        //Use default authentication policy
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
    }
    //Specify authentication policies for serverTrust objects
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

    if (self.SSLPinningMode == AFSSLPinningModeNone) {
        return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
    } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
        return NO;
    }
    //Processing different situations according to certificate verification strategy, digital signature authentication strategy and other authentication strategies
    switch (self.SSLPinningMode) {
        case AFSSLPinningModeNone://Not verifying public keys and certificates
        default:
            return NO;
        case AFSSLPinningModeCertificate: {//Verify the entire certificate
            NSMutableArray *pinnedCertificates = [NSMutableArray array];
            //Get the corresponding certificate object according to the specified certificate
            for (NSData *certificateData in self.pinnedCertificates) {
                [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
            }
            //Associate certificates with server Trust
            SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);

            if (!AFServerTrustIsValid(serverTrust)) {
                return NO;
            }

            // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
            //Get the server Trust certificate chain. Until the root certificate.
            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
            //If `pinned Certificates'contains the root certificate of the certificate chain corresponding to the `serverTrust' object. Returns true
            for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                    return YES;
                }
            }
            
            return NO;
        }
        case AFSSLPinningModePublicKey: {//Verify only digital signatures in certificates
            NSUInteger trustedPublicKeyCount = 0;
            //According to serverTrust object and SecPolicyCreateBasicX509 authentication strategy, the corresponding public key set is obtained.
            NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
            
            for (id trustChainPublicKey in publicKeys) {
                //Compare the acquired public key with the default public key acquired by the system, and if they are equal, they are authenticated.
                for (id pinnedPublicKey in self.pinnedPublicKeys) {
                    if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                        trustedPublicKeyCount += 1;
                    }
                }
            }
            return trustedPublicKeyCount > 0;
        }
    }
    
    return NO;
}

#pragma mark - NSKeyValueObserving

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

#pragma mark - NSSecureCoding

+ (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))];
}
#pragma mark - NSCopying
- (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;
}
@end

Last Original address,demo address.

Topics: iOS SSL Google Session Attribute