ios - 如何在我的 iOS 应用程序中使用 CFStream 将客户端 SSL 证书发送到服务器?

标签 ios ssl websocket certificate

我们有一个使用 SSL 的 WebSocket 安全服务器。我们希望在我们的iOS客户端中放置一个客户端SSL证书,以保证与服务器通信时的安全性。

因为我们使用的是 WebSocket,所以在 iOS 客户端中,我们使用 SocketRocket (Objective-C WebSocket 客户端库)实现WebSocket通信。

问题是我不知道如何将我的客户端 SSL 证书发送到服务器。

我可以设置 CFStream 的属性,比如 kCFStreamPropertySocketSecurityLevel。但我不知道它是如何工作的。而且我在 CFStream 中找不到任何关于证书的文档。

我知道当我们需要连接一个HTTPS服务器时,我们可以在NSURLConnection中使用didReceiveAuthenticationChallenge。但据我所知,CFStream 中没有对应的对象。

有人能有什么想法吗?

最佳答案

经过大量研究和尝试,我现在可以自己回答了。也希望对你有用。

其实我需要的是使用CFStream实现客户端SSL认证。所以我需要做这些:

  1. 将 PKCS #12 文件放入我的应用程序包
  2. 将此文件作为 NSData 读取到 pkcsData
  3. 使用方法SecPKCS12Import导入pkcsData
  4. 从上面导入的数据中获取身份和证书,并生成证书数组
  5. CFWriteStreamRefkCFStreamPropertySSLSettings 中将数组设置为关键字 kCFStreamSSLCertificates

示例代码如下:

  // Read .p12 file
  NSString *path = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
  NSData *pkcs12data = [[NSData alloc] initWithContentsOfFile:path];

  // Import .p12 data
  CFArrayRef keyref = NULL;
  OSStatus sanityChesk = SecPKCS12Import((__bridge CFDataRef)pkcs12data,
                                         (__bridge CFDictionaryRef)[NSDictionary
                                                                    dictionaryWithObject:@"123456"
                                                                    forKey:(__bridge id)kSecImportExportPassphrase],
                                         &keyref);
  if (sanityChesk != noErr) {
    NSLog(@"Error while importing pkcs12 [%ld]", sanityChesk);
  } else
    NSLog(@"Success opening p12 certificate.");

  // Identity
  CFDictionaryRef identityDict = CFArrayGetValueAtIndex(keyref, 0);
  SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict,
                                                                    kSecImportItemIdentity);

  // Cert
  SecCertificateRef cert = NULL;
  OSStatus status = SecIdentityCopyCertificate(identityRef, &cert);
  if (status)
    NSLog(@"SecIdentityCopyCertificate failed.");

  // the certificates array, containing the identity then the root certificate
  NSArray *myCerts = [[NSArray alloc] initWithObjects:(__bridge id)identityRef, (__bridge id)cert, nil];

  //
  [SSLOptions setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsExpiredRoots];
  [SSLOptions setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsExpiredCertificates];
  [SSLOptions setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsAnyRoot];
  [SSLOptions setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCFStreamSSLValidatesCertificateChain];
  [SSLOptions setObject:@"test.domain.com:443" forKey:(NSString *)kCFStreamSSLPeerName];
  [SSLOptions setObject:(NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(NSString*)kCFStreamSSLLevel];
  [SSLOptions setObject:(NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(NSString*)kCFStreamPropertySocketSecurityLevel];
  [SSLOptions setObject:myCerts forKey:(NSString *)kCFStreamSSLCertificates];
  [SSLOptions setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCFStreamSSLIsServer];

  [_outputStream setProperty:SSLOptions
                        forKey:(__bridge id)kCFStreamPropertySSLSettings];

因为我使用 SocketRocket,所以我在自己的分支中添加了这些代码:https://github.com/nickcheng/SocketRocket

关于ios - 如何在我的 iOS 应用程序中使用 CFStream 将客户端 SSL 证书发送到服务器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17018258/

相关文章:

javascript - 如何使用 JavaScript 从被拒绝的 WebSocket 打开握手中读取状态代码?

java - 同一 Tomcat 服务器上两个应用程序之间的双向 SSL

java - 如何信任我的自签名证书

google-app-engine - 在 Google Compute Engine 上扩展 WebSocket

iphone - 绘图线的优化,CAShapeLayer 的可能替代品

php - 如何使用流 stream_socket_client() 在 aruba 中打开 ssl 连接

websocket - OCaml Websocket 示例

javascript - 在 iOS 上加载时网页向下滚动一半

ios - react-native NavigatorIOS 没有按预期工作

ios - iPad分屏导致抽屉变暗