iOS 개발 에 대한 자세 한 설명-AFNetworking 으로 https 단 방향 검증,양 방향 검증 실현

12897 단어 ioshttps
애플 이 2017 년 1 월 1 일부 터 https 를 강제 사용 하기 시작 했다 고 발표 한 이래 htpps 는 사람들 이 토론 하 는 대상 중 하나 가 되 었 다.그 전에 https 가 나타 나 지 않 았 다 는 것 이 아니 라 개발 자 들 이 예상 하지 못 했 을 뿐이다.블 로 거들 은 15 년 동안 https 의 인 터 페 이 스 를 했 는데 이 구덩이 의 깊이 를 잘 알 고 있 기 때문이다.그 이 유 는 자신 이 이 분야 에 대해 잘 모 르 고 인터넷 의 자료 가 적 기 때문이다.이 밖 에 도 블 로그 가 옳 고 그 름 을 모 르 고 서로 퍼 져 당시 인터넷 에서 사용 할 수 있 는 코드 를 거의 찾 지 못 했다 는 점 에서 블 로 거들 의 말 은 과장 되 지 않 았 다.
이 를 감안 하여 블 로 거들 은 이 구 덩이 를 메 우 고 정확 한 코드 를 추가 하여 많은 개발 자 들 이 사용 할 수 있 도록 하려 고 했 으 나 나중에 방치 되 었 다.시 도 를 거 친 후에 블 로 거들 은 정 리 된 코드 를 여기에 발표 하여 애타게 찾 는 개발 자 에 게 도움 이 되 기 를 바란다.
1.오래된 AFNetworking 2.x 가 어떻게 이 루어 졌 는 지 먼저 말 해 보 자.
블 로 거들 은 인터넷 에서 몇 편의 게시 물 을 보 았 는데 그 중에서 말 하 는 방법 이 정확 하지만 모두 옳 은 것 은 아니다.그 몇 편의 블 로 그 는 거의 똑 같 기 때문에 블 로 거들 은 최초의 그 편 을 누가 썼 는 지 확정 할 수 없 기 때문에 다음 과 같은 방법 을 다시 설명 한다.
1)client.p12 인증 서 를 불 러 오기;
2)plist 파일 에서 그림 설정:

3)AFNetworking 에서 클래스 수정:

이 파일 을 찾 으 면 안에 방법 을 추가 합 니 다.

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

다시 한 가지 방법 을 수정 합 니 다.
NSURLConnection Delegate 의 동명 코드 를 아래 코드 로 교체 합 니 다.

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

}
4)요청

 NSString *url = @"xxxxxxxxxx";
  // 1.       
  AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
  //2  https   
  AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
  securityPolicy.allowInvalidCertificates = YES;
  mgr.securityPolicy = securityPolicy;
  // 3.  POST  

  [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);

  }];

이로써 기 존의 AFNetworking 은 https 인터페이스의 양 방향 검증 을 요청 하면 끝 났 습 니 다.그러나 문제 가 있 습 니 다.여기 서 AFNetworking 코드 를 바 꿔 야 합 니 다.게다가 새로운 AFNetworking 은 이미 있 습 니 다.코드 의 활력 을 유지 하기 위해 오래된 것 은 버 려 야 합 니 다.그리고 pods 를 업데이트 한 후에 반드시 바 꿀 코드 가 없어 지 는 것 도 문제 입 니 다.서 두 르 지 마 세 요.새로운 AFNetworking 을 어떻게 사용 하 는 지,pods 에 의 해 교체 코드 가 업데이트 되 는 문 제 를 해결 하 는 방법 을 알려 드 리 겠 습 니 다.
마지막 으로 한 가지 더 말씀 드 리 지만 오래된 AF 를 사용 하여 요청 합 니 다.client.p12 파일 만 사 용 했 고 server.cer 를 사용 하지 않 았 습 니 다.새로운 것 에서 유용 합 니 다.클 라 이언 트 가 모든 인증 서 를 신뢰 하 는 것 을 선택 한 것 으로 추정 되 고 일방적인 검증 이 되 었 습 니 다.
데 모 는 마지막 에.
2.새로운 AFNetworking 3.x 가 어떻게 이 루어 졌 는 지 말 해 보 자.
1)client.p12 와 server.cer 파일 을 불 러 옵 니 다.
2)plist 내의 설정,이것 은 위 와 같은 것 입 니 다:

3)여 기 는 클래스 의 코드 를 수정 할 필요 가 없 지만,여 기 는 방법 을 다시 써 야 합 니 다.

 NSString *url = @"https://test.niuniuhaoguanjia.com/3.0.0/?service=City.GetCityList";

  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];
  //          r
  _manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
  [_manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
    NSLog(@"setSessionDidBecomeInvalidBlock");
  }];
  //           setSessionDidReceiveAuthenticationChallengeBlock   
  __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;
  }];

4)요청

//               ,    
  [_manager GET: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);
  }];

또 하나의 방법 을 더 해 야 한다.

+(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;
}

맞 아,우 리 는 포장 을 해 야 하 는데 어떻게 포장 해 야 하지?블 로 거들 은 집중 을 시 도 했 지만 모두 실 패 했 습 니 다.정말 이해 할 수 없습니다.주동 적 으로 포장 을 하 는 개발 자 들 도 포장 을 한 후에 실 패 를 요청 하 는 문제 에 부 딪 힐 것 이 라 고 믿 습 니 다.성공 할 수도 있 습 니 다.하지만 여기 서 block 에서 변 수 를 사용 하 는 문 제 를 주의해 야 합 니 다.구체 적 으로 블 로 거들 이 어떻게 포장 하 는 지 볼 수 있 습 니 다.
여기까지 새로운 AF 요청 https 가 끝 났 습 니 다.봉 인 된 것 을 보고 싶 으 면 Demo 를 마지막 에 놓 으 세 요.
3.단 방향 검증
이 쯤 되면 인터넷 의 많은 방법 을 말 하지 않 을 수 없다.모두 단 방향 검증 을 양 방향 으로 하 는데 사실은 그 원 리 를 이해 하지 못 한다.원리 에 대해 서 는 여 기 를 보 세 요.
코드 구현 AF 는 모두 같 습 니 다:

//AF          
  _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;
}

4.데모 다운로드 혜택
인증서 보안 문제 로 데모 의 인증서 블 로 거들 이 삭 제 했 습 니 다.양 해 를 바 랍 니 다.자신의 인증 서 를 넣 어 주 십시오.
오래된 AF 방문 httpsDemo
새로운 AF httpsDemo 방문
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기