iOS AFNetworking Framework HTTPS Request Configuration

Keywords: iOS JSON Android Java

iOS, under Apple's mandatory requirements, data transmission must be in accordance with the ATS(App Transefer Security) clause. About AFNetworking Framework Transfer HTTPS Data.
 

I. NSAllows Arbitrary Loads Whitelist Mechanism

NSAllows ArbitraryLoads is a product of ATS promotion, and of course it can last a long time. To bypass ATS, you need to configure info.plist file.

<key>NSAppTransportSecurity</key>
<dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
 </dict>

This mechanism actually allows access to all HTTP and HTTPS, which is obviously dangerous in practice. Setting false avoids bypassing ATS. The question is, do we really need to turn this option off altogether?

For example, in some file servers, the configuration of HTTPS on CDN server will affect the transmission speed. In this case, HTTP has a high advantage. Therefore, for HTTPP transmission of such servers, we can also use the following way (setting up a whitelist), whitelist must be used outside the HTTPS protocol.

 <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>lib.baidu.com</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
            </dict>
            <key>oss.fastdfs.cn</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
            </dict>
           </dict>
   </dict>

 

II. Certificate-free verification

Certificate-free authentication, generally speaking, client certificate library does not verify the certificates transmitted by server.

For instance

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//Certificates granted by non-authoritative bodies
manager.securityPolicy.allowInvalidCertificates = YES;
//Domain name consistency is not validated
manager.securityPolicy.validatesDomainName = NO;
//Close the cache to avoid interference testing
manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

[manager GET:@"https://www.baidu.com/s?wd=https" parameters:nil progress:nil 
success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"%@",responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"%@",error);
}];

This approach makes our app vulnerable to man-in-the-middle attacks, not entirely because we set allowInvalidCertificates=YES and validates DomainName = NO, but because there is no server CA Certificate in the local certificate library, as long as any https can be forged as a server.

Therefore, we do not recommend this approach.

 

Certificate Verification

3.1 Classification of Encryption Standards

Certificate verification can be divided into two categories: one-way authentication and two-way authentication.

In addition, we need to distinguish between certificates and certificate libraries. Certificate libraries include PKCS12,JKS,BKS, etc. Among them, PKCS12 is an Internet standard, which can be cross-platform and cross-language (supporting Android,iOS,WP,PC,JAVA,PHP...). JKS is a Java standard, which can only be used in Java platform and Java language, and BKS is Bouncy Castle Company. Encryption standard, as a complement to the Android platform supporting the SSL/TLS encryption suite PKCS12, has a high encryption intensity, but is currently only used for Android platform. Certificate is defined as the digital signature information stored in the certificate library or a separate certificate file, such as crt,pem,cer and so on. Before we talk about encryption, let's look at the self-signed certificate.

 

3.2 Self-signed Certificate vs Third Party Authoritative Authority Certificate

Some may wonder whether self-signed certificates have the same effect as certificates of third-party authorities.

In fact, I think it's exactly the same. In the Internet, in the service based on B/S architecture, the B-end usually imports the root certificate of the third-party authority (ROOT CA), which is actually to verify whether the CA certificate of the website is secure and issued by such authority. The problem is that our App and Server are C/S architectures, and each app can only access a limited number of Web sites at most. The app we make ourselves actually trusts the site URL s we set up. That is to say, we believe in ourselves. Our certificates are only for data security transmission and are not attacked by intermediaries, so there is no need to use ROOT CA at all, but specifically, no one has tried to verify this method so far.

I tried to find a third-party CA certificate authority to communicate. It seems that he just said that this is Apple's regulation. In fact, self-signed certificates are essentially no problem, as long as the key length, complexity, encryption methods meet the requirements.

Therefore, if Apple really requires the use of third-party CA ROOT signature rules, itself is unreasonable. These problems can't be avoided, but if Apple allows self-signed certificates, set allowInvalidCertificates=YES.

 

3.3 One-way authentication

Documents to be prepared: Server-side certificate repository, server-side export certificate

One-way authentication, in fact, only the Client side verifies the certificate of the Server side, and the Server does not need to verify the certificate of the Client side.

Custom class: MyAFNetworking

+ (AFHTTPSessionManager *)manager;
{
    static AFHTTPSessionManager *shareInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        shareInstance = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:BaseHttpURLString] sessionConfiguration:configuration];
        //Setting the type of request parameter: JSON
        shareInstance.requestSerializer = [AFJSONRequestSerializer serializer];
        //Set the type of result returned by the server: JSON (AFJSON Response Serializer)
        shareInstance.responseSerializer = [AFJSONResponseSerializer serializer];
        //Set the timeout time of the request
        shareInstance.requestSerializer.timeoutInterval = 20.0f;
        //Setting ContentType
        shareInstance.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/plain", @"text/javascript", @"text/xml", @"image/jpeg",@"image/png", nil];

        // https configuration
        NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"Your certificate name" ofType:@"cer"];
        NSData *certData = [NSData dataWithContentsOfFile:cerPath];
        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[[NSSet alloc] initWithObjects:certData, nil]];

        NSSet *dataSet = [[NSSet alloc] initWithObjects:certData, nil]; //Multiple server certificates can be added here

        // SetPinned Certificates Setup Certificates File (probably more than one certificate)
        [securityPolicy setPinnedCertificates:dataSet];
        // AllowInvalid Certificates Allow Invalid Certificates
        [securityPolicy setAllowInvalidCertificates:NO];
        // Domain Name Validates Domain Name Need to Verify Domain Name
        [securityPolicy setValidatesDomainName:YES];

        shareInstance.securityPolicy = securityPolicy;
    });
    return shareInstance;
}

Note: The certificates mentioned above are CERs or crt certificates from the server side everywhere. These certificates are binary encoding certificates in X509 Der format, not Base64 encoding certificates in X509 PAM format. For the generation of self-signed certificates, please refer to the address below.

Common Certificate Formats and Mutual Conversion 

iOS Asymmetric Encryption and Decryption

iOS self-signed certificate establishment

 

3.4 Two-way authentication

Like Android, the client certificate library type can be a pfx certificate of PKCS12 type, which contains private key, public key and certificate, and is passworded.

Two-way certification is generally used for products with high security requirements, such as financial apps, government apps and other special industries.

Documents to be prepared: Server Certificate Library, Server Certificate Trust Library, Server Exported Certificate, Client Certificate Library, Client Certificate

Note [1]: The server certificate library can use the same certificate library as the server certificate library. The only thing to do is to import the client certificate.

Note [2]: Client certificates generally use cross-platform PKCS12 certificate repositories (pfx or p12), which must remember the key of the certificate repository. Such certificate repositories contain both private key, public key and certificate.

 

3.4.1 Trust Server

This step is used to verify the client certificate, exactly the same as one-way authentication.

Custom class: MyAFNetworking

+ (AFHTTPSessionManager *)manager;
{
    static AFHTTPSessionManager *shareInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        shareInstance = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:BaseHttpURLString] sessionConfiguration:configuration];
        //Setting the type of request parameter: JSON
        shareInstance.requestSerializer = [AFJSONRequestSerializer serializer];
        //Set the type of result returned by the server: JSON (AFJSON Response Serializer)
        shareInstance.responseSerializer = [AFJSONResponseSerializer serializer];
        //Set the timeout time of the request
        shareInstance.requestSerializer.timeoutInterval = 20.0f;
        //Setting ContentType
        shareInstance.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/plain", @"text/javascript", @"text/xml", @"image/jpeg",@"image/png", nil];

        // https configuration
        NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"Your certificate name" ofType:@"cer"];
        NSData *certData = [NSData dataWithContentsOfFile:cerPath];
        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[[NSSet alloc] initWithObjects:certData, nil]];

        NSSet *dataSet = [[NSSet alloc] initWithObjects:certData, nil]; //Multiple server certificates can be added here

        // SetPinned Certificates Setup Certificates File (probably more than one certificate)
        [securityPolicy setPinnedCertificates:dataSet];
        // AllowInvalid Certificates Allow Invalid Certificates
        [securityPolicy setAllowInvalidCertificates:NO];
        // Domain Name Validates Domain Name Need to Verify Domain Name
        [securityPolicy setValidatesDomainName:YES];

        shareInstance.securityPolicy = securityPolicy;
    });
    return shareInstance;
}

3.4.2 Provide Client Certificate and Certificate Library

/*
*
**
* Creating authentication conditions for server trusted clients
**
*/
+(AFHTTPSessionManager *) createCredentialsClient
{
__block AFHTTPSessionManager * manager = [MyAFNetworking manager];
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __autoreleasing NSURLCredential *credential =nil;
    if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        if([manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
            credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            if(credential) {
                disposition =NSURLSessionAuthChallengeUseCredential;
            } else {
                disposition =NSURLSessionAuthChallengePerformDefaultHandling;
            }
        } else {
            disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
        }
    } else {
        // client authentication
        SecIdentityRef identity = NULL;
        SecTrustRef trust = NULL;
        NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"pfx"];
        NSFileManager *fileManager =[NSFileManager defaultManager];

        if(![fileManager fileExistsAtPath:p12])
        {
            NSLog(@"client.p12:not exist");
        }
        else
        {
            NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
            #Load PKCS12 certificates, pfx or p12
            if ([MyAFNetworking extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
            {
                SecCertificateRef certificate = NULL;
                SecIdentityCopyCertificate(identity, &certificate);
                const void*certs[] = {certificate};
                CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
                credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge  NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
                disposition =NSURLSessionAuthChallengeUseCredential;
            }
        }
    }
    *_credential = credential;
    return disposition;
}];

return manager;
}

/**
**Load PKCS12 certificates, pfx or p12
** 
**/
+(BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
    OSStatus securityError = errSecSuccess;
    //client certificate password
    NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"Your p12 Password"
                                                                forKey:(__bridge id)kSecImportExportPassphrase];

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);

    if(securityError == 0) {
        CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
        const void*tempIdentity =NULL;
        tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
        *outIdentity = (SecIdentityRef)tempIdentity;
        const void*tempTrust =NULL;
        tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
        *outTrust = (SecTrustRef)tempTrust;
    } else {
        NSLog(@"Failedwith error code %d",(int)securityError);
        return NO;
    }
    return YES;
}

Through the above way, we can achieve two-way authentication.

AFHTTPSessionManager * manager = [MyAFNetworking  createCredentialsClient];

 

Reference Blog

Principle and Implementation of HTTPS Bidirectional Authentication (Nginx + iOS)

AFNetworking 3.0 and Service-side Self-Signing Certificate https Bidirectional Authentication

iOS Source Sharing--HTTPS Configuration Chapter

HTTPS for iOS Application Network Security

Solutions for iOS 9 & iOS 10 HTTP not working properly

iOS Development-https Certificate-free Verification

Posted by sfx81 on Wed, 10 Jul 2019 15:25:12 -0700