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

nic*_*eng 5 ssl certificate websocket ios

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

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

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

我可以像设置CFStream的属性一样kCFStreamPropertySocketSecurityLevel.但我不知道它是如何工作的.我在CFStream中找不到任何关于证书的文档.

我知道当我们需要连接到HTTPS服务器时,我们可以didReceiveAuthenticationChallenge在NSURLConnection中使用.但据我所知,CFStream中没有对应物.

有人有什么想法吗?

nic*_*eng 12

经过大量的学习和尝试,我现在可以自己回答.也希望它对你有用.

实际上,我需要的是使用CFStream实现客户端SSL身份验证.所以我需要这样做:

  1. 将PKCS#12文件放入我的应用包中
  2. 阅读这个文件NSDatapkcsData
  3. 使用方法SecPKCS12Import导入pkcsData
  4. 从上面导入的数据中获取身份和证书,并生成证书数组
  5. 设置阵列键入kCFStreamSSLCertificateskCFStreamPropertySSLSettings的您CFWriteStreamRef

示例代码如下:

  // 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];
Run Code Online (Sandbox Code Playgroud)

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