详解iOS开发 - 用AFNetworking实现https单向验证,双向验证

2020-01-18 18:52:58于海丽

自苹果宣布2017年1月1日开始强制使用https以来,htpps慢慢成为大家讨论的对象之一,不是说此前https没有出现,只是这一决策让得开发者始料未及,博主在15年的时候就做过https的接口,深知此坑之深,原因就是自身对这方面知识不了解加上网上的资料少,除此外还有博客不知对错就互相转载,导致当时网上几乎找不到能用的代码,这一点,博主说的毫不夸张。

鉴于此,博主一直想填一下这个坑,多增加一些正确的代码,来供广大开发者使用,后来一直被搁置,经过尝试后,博主现将整理好的代码发布在这里,希望能帮到焦急寻找的开发者。

1.先来说说老的AFNetworking2.x怎么来实现的

博主在网上看过几篇帖子,其中说的一些方法是正确的,但是却并不全对,由于那几篇博客几乎一样,博主不能确定最早的那篇是谁写的,所以就重新在下面说明下方法:

1)倒入client.p12证书;

2)在plist文件做如图配置:

ios开发,单向https,ios,https双向验证,os,https双向认证

3)在AFNetworking中修改一个类:

ios开发,单向https,ios,https双向验证,os,https双向认证

找到这个文件,在里面增加一个方法:


- (OSStatus)extractIdentity:(CFDataRef)inP12Data toIdentity:(SecIdentityRef*)identity { 
  OSStatus securityError = errSecSuccess;
  CFStringRef password = CFSTR("证书密码"); 
  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;
}

再修改一个方法:

用下面的这段代码替换NSURLConnectionDelegate中的同名代码,


- (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];

}