[Quoted from IamOkay's blog] Under Apple's mandatory requirement, data transmission on iOS must comply with the ATS (App Transefer Security) terms. About AFNetworking framework transmitting HTTPS data. 1. AllowsArbitraryLoads whitelist mechanism NSAllowsArbitraryLoads is a product of the ATS promotion process. Of course, it may last for a long time or even forever. In order to access HTTP services, it is generally necessary to bypass ATS restrictions and configure the info.plist file. - < key >NSAppTransportSecurity</ key >
- <dict>
- < key >NSAllowsArbitraryLoads</ key >
- < true />
- </dict>
This mechanism actually allows all HTTP and HTTPS access, which is obviously dangerous. Setting it to false can avoid bypassing ATS, but the question is, do we really need to turn this option off completely? For example, for some file servers and CDN servers, configuring HTTPS will affect the transmission speed. In this case, HTTP is much more advantageous. Therefore, for HTTP transmission of such servers, we can also use the following method (setting a whitelist), and those outside the whitelist must use 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>
2. Certificate-free verification Certificate verification is free. Generally speaking, the client certificate library will not verify the certificate transmitted by the server. For example - AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
- //Allow certificates issued by non-authoritative organizations
- manager.securityPolicy.allowInvalidCertificates = YES;
- //Does not verify domain name consistency
- manager.securityPolicy.validatesDomainName = NO ;
- // Turn off the cache to avoid interfering with the test
- 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 method makes our app vulnerable to man-in-the-middle attacks. The reason is not entirely because we set allowInvalidCertificates=YES and validatesDomainName=NO, but because the server's CA certificate is not added to the local certificate library. Any https can be forged as a server. Therefore, we do not recommend using this method. 3. Certificate Verification 3.1 Classification of encryption standards Certificate verification is divided into two categories: one-way authentication and two-way authentication. In addition, we also need to distinguish the difference between certificates and certificate libraries. Certificate library types include PKCS12, JKS, BKS, etc. Among them, PKCS12 is an Internet standard that can be cross-platform and cross-language (supporting Android, iOS, WP, PC, JAVA, PHP...), JKS is a Java standard that can only be used on the Java platform and Java language, and BKS is the encryption standard of Bouncy Castle. As a supplement to the Android platform's support for SSL/TLS encryption suite PKCS12, its encryption strength is very high, but it is currently only used on the Android platform. The definition of a certificate is the digital signature information stored in the certificate library or a separate certificate file, such as crt, pem, cer, etc. Before talking about encryption, let's take a look at self-signed certificates. 3.2 Self-signed certificate vs. third-party authority certificate Some people may wonder whether the effectiveness of self-signed certificates and certificates from third-party authorities is the same? In fact, I think it is exactly the same. On the Internet, in services based on B/S architecture, the B side usually imports the root certificate (ROOT CA) of a third-party authority. In fact, it is to verify whether the CA certificate of the website is safe and whether it is issued by an authoritative security agency. The problem is that our App and Server are C/S architectures, and each app can only access a limited number of URLs at most. The app we make ourselves actually trusts the site URL we set, which means that we trust ourselves. Our certificate is only for secure data transmission and not being attacked by a man-in-the-middle, so there is no need to use (ROOT CA), but specifically, no one has tried this method so far to see if it can pass the audit. I tried to communicate with a third-party CA certificate authority, and it seemed that they just said that this was Apple's regulation. In fact, there is nothing wrong with self-signed certificates in essence, as long as the key length, complexity, encryption method, etc. meet the requirements. Therefore, if Apple really requires the use of a third-party CA ROOT signature rule, it is unreasonable. These problems cannot be avoided, but if Apple allows self-signed certificates, set allowInvalidCertificates=YES. 3.3 One-way Authentication Files to prepare: server certificate library, server-exported certificate One-way authentication actually means that only the client verifies the server's certificate, and the server does not need to verify the client's certificate. 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];
- //Set the request parameter type: JSON
- shareInstance.requestSerializer = [AFJSONRequestSerializer serializer];
- //Set the type of the server response: JSON (AFJSONResponseSerializer, AFHTTPResponseSerializer)
- shareInstance.responseSerializer = [AFJSONResponseSerializer serializer];
- //Set the request timeout
- shareInstance.requestSerializer.timeoutInterval = 20.0f;
- //Set 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]; //You can add multiple server certificates here
-
- // setPinnedCertificates sets the certificate file (maybe more than one certificate)
- [securityPolicy setPinnedCertificates:dataSet];
- // allowInvalidCertificates whether to allow invalid certificates
- [securityPolicy setAllowInvalidCertificates: NO ];
- // validatesDomainName whether to verify the domain name
- [securityPolicy setValidatesDomainName:YES];
-
- shareInstance.securityPolicy = securityPolicy;
- });
- return shareInstance;
- }
Note: The certificate mentioned above is the cer or crt certificate obtained from the server. This type of certificate is a binary encoded certificate in the X509 Der format, not a Base64 encoded certificate in the X509 PAM format. For the generation of self-signed certificates, please refer to the following address Common certificate formats and conversions iOS asymmetric encryption and decryption iOS self-signed certificate creation (self-signed) 3.4 Two-way authentication Like iOS and Android, the client certificate store type can be a PKCS12 type pfx certificate, which contains a private key, public key, and certificate, and is protected by a password. Two-way authentication is generally used for products with higher security requirements, such as financial apps, government apps and other special industries. Files to prepare: server certificate library, server certificate trust library, server exported certificate, client certificate library, client certificate Note [1]: The server certificate store can use the same certificate store as the server trust certificate store. The only thing to do is to import the client certificate. Note [2]: Client certificates generally use a cross-platform PKCS12 certificate store (pfx or p12). The certificate store key must be remembered. This type of certificate store contains private keys, public keys, and certificates. 3.4.1 Trusting the Server This step is used to verify the client certificate, which is 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];
- //Set the request parameter type: JSON
- shareInstance.requestSerializer = [AFJSONRequestSerializer serializer];
- //Set the type of the server response: JSON (AFJSONResponseSerializer, AFHTTPResponseSerializer)
- shareInstance.responseSerializer = [AFJSONResponseSerializer serializer];
- //Set the request timeout
- shareInstance.requestSerializer.timeoutInterval = 20.0f;
- //Set 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]; //You can add multiple server certificates here
-
- // setPinnedCertificates sets the certificate file (maybe more than one certificate)
- [securityPolicy setPinnedCertificates:dataSet];
- // allowInvalidCertificates whether to allow invalid certificates
- [securityPolicy setAllowInvalidCertificates: NO ];
- // validatesDomainName whether to verify the domain name
- [securityPolicy setValidatesDomainName:YES];
-
- shareInstance.securityPolicy = securityPolicy;
- });
- return shareInstance;
- }
3.4.2 Providing client certificates and certificate libraries - /*
- *
- **
- * Create authentication conditions for the server to trust the client
- **
- */
- +(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 certificate, 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 certificate, 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 methods, we can achieve two-way authentication - AFHTTPSessionManager * manager = [MyAFNetworking createCredentialsClient];
|