【问题标题】:iOS - SSL Handshake - Client Certificate - NSURLSession - NSURLErrorDomain Code=-1200 - Wrong chainiOS - SSL 握手 - 客户端证书 - NSURLSession - NSURLErrorDomain 代码=-1200 - 错误链
【发布时间】:2017-05-07 02:17:15
【问题描述】:

我正在尝试使用带有 Objective C 的 iPhone 调用 Web 服务,但出现以下错误:

error -->Error Domain=NSURLErrorDomain Code=-1200 "一个 SSL 错误 发生并且无法与服务器建立安全连接。” 用户信息={_kCFStreamErrorCodeKey=-9806, NSLocalizedRecoverySuggestion=你想连接到服务器吗 无论如何?,NSUnderlyingError=0x600000243690 {错误 Domain=kCFErrorDomainCFNetwork 代码=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9806, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9806}}, NSLocalizedDescription=发生 SSL 错误并且安全 无法连接到服务器。, NSErrorFailingURLKey=https://./, NSErrorFailingURLStringKey=https://./, _kCFStreamErrorDomainKey=3}

Info.plist 配置

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

服务器信息:

TLS 1.2
客户端证书身份验证要求开启
密码列表:

  • TLS_RSA_WITH_AES_256_CBC_SHA256
  • TLS_RSA_WITH_AES_128_CBC_SHA256
  • TLS_RSA_WITH_AES_256_CBC_SHA
  • TLS_RSA_WITH_AES_128_CBC_SHA
  • TLS_RSA_WITH_3DES_EDE_CBC_SHA

注意:在客户端(iOS)和服务器中间添加管理客户端证书的charles代理后,请求成功完成。因此我可以断定我使用的客户端证书是有效的。

任何建议我如何解决这个问题或任何线索我做错了哪个配置?

我的猜测是关于客户端证书配置,因为我已经在使用完全相同的配置和类似的服务(ssl 层/服务器密码),它不需要客户端证书身份验证。

使用的代码:

- (void)callWebService:(NSString *)urlStr
             operation:(NSString *)operation
                  body:(NSString *)body
              resolver:(RCTPromiseResolveBlock)resolve
              rejecter:(RCTPromiseRejectBlock)reject {

  NSLog(@"Starting callWebservice");

  NSURL *url = [NSURL URLWithString:urlStr];
  NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
  NSString *msgLength = [NSString stringWithFormat:@"%lu", (unsigned long) [body length]];

  [theRequest addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
  [theRequest addValue:msgLength forHTTPHeaderField:@"Content-Length"];
  [theRequest addValue:operation forHTTPHeaderField:@"SOAPAction"];
  [theRequest addValue:@"iOS" forHTTPHeaderField:@"User-Agent"];
  [theRequest setHTTPMethod:@"POST"];
  [theRequest setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];

  NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
  NSURLSession *session = [NSURLSession sessionWithConfiguration: defaultConfigObject
                                                        delegate: self
                                                   delegateQueue: [NSOperationQueue mainQueue]];


  NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:theRequest
                                              completionHandler:^(NSData *responseData, NSURLResponse *response, NSError *error)
                                    {
                                      NSLog(@"Completed request");

                                      if (error != nil) {
                                        NSLog(@"error -->%@", error);
                                        reject(@"Auth error", @"There were authentication errors", error);
                                      } else {
                                        NSString *responseStr = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
                                        NSLog(@"-->%@", responseStr);
                                        resolve(responseStr);
                                      }


                                    }];
  [dataTask resume];

}

- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
  NSLog(@"didReceiveChallenge: %@", challenge.protectionSpace.authenticationMethod);

  if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {

    NSLog(@"challenged for client certificate");

      NSString *sslCertName = @"certificate_name";
      NSString *path2 = [[NSBundle mainBundle] pathForResource:sslCertName ofType:@"p12"];
      NSData *p12data = [NSData dataWithContentsOfFile:path2];

      CFDataRef inP12data = (__bridge CFDataRef) p12data;

      SecIdentityRef myIdentity;
      SecTrustRef myTrust;

      extractIdentityAndTrust(inP12data, &myIdentity, &myTrust);

      SecCertificateRef myCertificate;
      SecIdentityCopyCertificate(myIdentity, &myCertificate);
      const void *certs[] = {myCertificate};
      CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL);

      CFRelease(myCertificate);

      secureCredential = [NSURLCredential credentialWithIdentity:myIdentity
                                                    certificates:(__bridge NSArray *) certsArray
                                                     persistence:NSURLCredentialPersistencePermanent];

    CFRelease(certsArray);

    [[challenge sender] useCredential:secureCredential forAuthenticationChallenge:challenge];

    completionHandler(NSURLSessionAuthChallengeUseCredential, secureCredential);
  }

  else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

    NSLog(@"challenged for server trust");

    [challenge.sender useCredential:[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]
         forAuthenticationChallenge: challenge];

    completionHandler(NSURLSessionAuthChallengeUseCredential,
                      [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
  } else {

    NSLog(@"other challenge");

    if ([challenge previousFailureCount] == 0) {

      [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];

    } else {

      [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
      completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,nil);
    }

  }
}


OSStatus extractIdentityAndTrust(CFDataRef inP12data, SecIdentityRef *identity, SecTrustRef *trust)
{
  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 myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
    const void *tempIdentity = NULL;
    tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
    *identity = (SecIdentityRef)tempIdentity;
    const void *tempTrust = NULL;
    tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
    *trust = (SecTrustRef)tempTrust;
  }

  if (options) {
    CFRelease(options);
  }

  return securityError;
}

使用额外信息进行编辑

经过一些wireshark调查,iOS似乎发送了两次客户端证书,服务器关闭了连接。

尝试了此处指定的解决方案:http://oso.com.pl/?p=207&lang=en 但它破坏了我的所有代码

【问题讨论】:

  • 您的应用传输安全配置是什么?
  • 谢谢,忘记在帖子中添加配置。我使用以下配置禁用它: NSAllowsArbitraryLoads

标签: ios objective-c iphone ssl client-certificates


【解决方案1】:

目前的解决方案

在比较成功请求 (android) 和 iOS 失败请求之间的 wireshark 数据包后,我注意到 iOS 单独发送客户端证书,而 Android 和 Charles Proxy 发送客户端证书完整链。

我仍在调查未在 iOS 上发送完整链的原因。可能是因为它不信任签署客户端证书的中间证书和根证书?

无论如何,我通过使用以下代码敲击发送的链,设法让 SSL 握手在 iOS 上工作:

  NSString *sslCertName = @"teste_webservices";
  NSString *path2 = [[NSBundle mainBundle] pathForResource:sslCertName ofType:@"p12"];
  NSData *p12data = [NSData dataWithContentsOfFile:path2];

  CFDataRef inP12data = (__bridge CFDataRef) p12data;

  SecIdentityRef myIdentity;
  SecTrustRef myTrust;

  extractIdentityAndTrust(inP12data, &myIdentity, &myTrust);


  NSData *ca1CertData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ca1" ofType:@"cer"]];
  SecCertificateRef ca1CertRef = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef) ca1CertData);
  NSData *rootCertData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"rootCa" ofType:@"cer"]];
  SecCertificateRef rootCertRef = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef) rootCertData);


  SecCertificateRef myCertificate;
  SecIdentityCopyCertificate(myIdentity, &myCertificate);
  const void *certs[] = {ca1CertRef, rootCertRef};

  CFArrayRef certsArray = CFArrayCreate(NULL, certs, 2, NULL);

  CFRelease(myCertificate);

  secureCredential = [NSURLCredential credentialWithIdentity:myIdentity
                                                certificates:(__bridge NSArray *) certsArray
                                                 persistence:NSURLCredentialPersistencePermanent];

【讨论】:

    猜你喜欢
    • 2016-01-18
    • 1970-01-01
    • 2016-01-18
    • 2018-05-15
    • 2016-06-15
    • 2015-01-02
    • 1970-01-01
    • 2014-11-13
    • 1970-01-01
    相关资源
    最近更新 更多