HTTPS related

HTPPS have been a topic of discussion since Apple announced the mandatory use of HTTPS on January 1, 2017. It’s not that HTTPS didn’t exist before, it’s just that the decision caught developers by surprise. Bloggers have been using HTTPS for 15 years and know the pitfalls. The reason is that they do not understand the knowledge of this aspect and there is little information on the Internet, in addition to the bloggers do not know right or wrong to reprint each other, leading to the Internet at that time almost can not find the use of the code, this point, the blogger said without exaggeration.

In view of this, the blogger has been trying to fill this hole, add some more correct code, for the use of the majority of developers, later has been shelved, after trying, the blogger is now finishing the code published here, hoping to help anxiously looking for developers.

Afnetworking2.x (afNetworking2.x, afNetworking2.x, afNetworking2.x, afNetworking2.x, afNetworking2.x, afNetworking2.x)

1) Import the client.p12 certificate.

2) Do the following configuration in the PList file:

3) Modify a class in AFNetworking:



Find this file and add a method to it:

- (OSStatus)extractIdentity:(CFDataRef)inP12Data toIdentity:(SecIdentityRef*)identity { OSStatus securityError = errSecSuccess; CFStringRef password = CFSTR(" certificate password "); const void *keys[] = { kSecImportExportPassphrase }; const void *values[] = { password }; CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); securityError = SecPKCS12Import(inP12Data, options, &items); if (securityError == 0) { CFDictionaryRef ident = CFArrayGetValueAtIndex(items,0); const void *tempIdentity = NULL; tempIdentity = CFDictionaryGetValue(ident, kSecImportItemIdentity); *identity = (SecIdentityRef)tempIdentity; } if (options) { CFRelease(options); } return securityError; }Copy the code

Modify one more method:

Replace the code of the same name in NSURLConnectionDelegate with the following code,  - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { NSString *thePath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]; NSLog(@"thePath===========%@",thePath); NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath]; CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data; SecIdentityRef identity = NULL; // extract the ideneity from the certificate [self extractIdentity :inPKCS12Data toIdentity:&identity]; SecCertificateRef certificate = NULL; SecIdentityCopyCertificate (identity, &certificate); const void *certs[] = {certificate}; // CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL); // create a credential from the certificate and ideneity, then reply to the challenge with the credential //NSLog(@"identity=========%@",identity); NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:nil persistence:NSURLCredentialPersistencePermanent]; // credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent]; [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; }Copy the code

4) Initiate a request

NSString *url = @"xxxxxxxxxx"; / / 1. Get the request managers AFHTTPRequestOperationManager * MGR = [AFHTTPRequestOperationManager manager]; / / 2 set HTTPS requests AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode: AFSSLPinningModeCertificate]; securityPolicy.allowInvalidCertificates = YES; mgr.securityPolicy = securityPolicy; // 3. Send a POST request [MGR POST: URL parameters:nil Success :^(AFHTTPRequestOperation * _Nonnull Operation, id _Nonnull responseObject) { NSLog(@"responseObject: %@", responseObject); } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) { NSLog(@"Error: %@", error); }];Copy the code

However, there is one problem. AFNetworking code needs to be changed. Besides, the new AFNetworking code is already available, and the old one should be discarded in order to keep the code dynamic. In addition, there is no code that must be replaced after updating PODS, which is also a problem. Don’t worry, here is how to use the new AFNetworking and solve the problem of code replacement by updating PODS.

Finally, using the old AF request, which uses only the client.p12 file and does not use server.cer, is useful in the new one, assuming that the client chooses to trust any certificate, it becomes one-way authentication.

The Demo is at the end

2. How is the new AFNetworking3.x implemented

1) Import the client.p12 and server.cer files

2) Setup inside plist, which is the same as above:

3) There is no need to change the code inside the class, but there is a method that needs to be overridden:

NSString *url = @"https:/xxxxxxxxxxxxxxxxx"; NSString *certFilePath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"]; NSData *certData = [NSData dataWithContentsOfFile:certFilePath]; NSSet *certSet = [NSSet setWithObject:certData]; AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:certSet]; policy.allowInvalidCertificates = YES; policy.validatesDomainName = NO; _manager = [AFHTTPSessionManager manager]; _manager.securityPolicy = policy; _manager.requestSerializer = [AFHTTPRequestSerializer serializer]; _manager.responseSerializer = [AFHTTPResponseSerializer serializer]; _manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/plain", nil]; / / close the cache to avoid interference test r _manager. RequestSerializer. CachePolicy = NSURLRequestReloadIgnoringLocalCacheData; [_manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) { NSLog(@"setSessionDidBecomeInvalidBlock"); }]; / / client request rewrite setSessionDidReceiveAuthenticationChallengeBlock method __weak typeof (self) weakSelf = self; [_manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) { NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; __autoreleasing NSURLCredential *credential =nil; if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { if([weakSelf.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:@"p12"]; NSFileManager *fileManager =[NSFileManager defaultManager]; if(![fileManager fileExistsAtPath:p12]) { NSLog(@"client.p12:not exist"); } else { NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12]; if ([[weakSelf class]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; }];Copy the code

About the code, and the old AF changes inside the truth actually is the same, may have a look of this article: www.jianshu.com/p/9e573607b…

4) Initiate a request

// The third step and this step of the code together, [_manager :url parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil]; NSLog(@"JSON: %@", dic); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"Error: % @ ", error); NSData *data = [error.userInfo objectForKey:@"com.alamofire.serialization.response.error.data"]; NSString *str =  [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",str); }];Copy the code

Add another method:

+(BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data { OSStatus securityError = errSecSuccess; //client certificate password NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@" 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; }Copy the code

Here you will find http://www.jianshu.com/p/9e573607be13 code is redundant, that’s right, we are want to encapsulate the, specific how can go to the blogger encapsulation.


At this point, the new AF request for HTTPS is finished. If you want to see the package, the Demo comes last.

3. One-way verification Speaking of this, we have to say that many methods on the Internet, one-way verification as a two-way, in fact, do not understand its principle, about the principle, see here code implementation AF are the same:

// First, your server does not have two-way authentication, otherwise the following code is useless. //AF adds this sentence and the following method _manager.securityPolicy = [self customSecurityPolicy]; /**** SSL Pinning ****/ - (AFSecurityPolicy*)customSecurityPolicy { NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"]; NSData *certData = [NSData dataWithContentsOfFile:cerPath]; AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; [securityPolicy setAllowInvalidCertificates:YES]; NSSet *set = [NSSet setWithObjects:certData, nil]; [securityPolicy setPinnedCertificates:@[certData]]; /**** SSL Pinning ****/ return securityPolicy; }Copy the code

4.Demo download benefits

Due to the certificate security problem, the certificate blogger in Demo has been deleted, please forgive me, please add your certificate. The old AF accesses httpsDemo

The new AF accesses httpsDemo