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



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

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

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

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

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

谁有什么主意?

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

实际上,我需要的是通过使用CFStream实现客户端SSL认证。所以我需要这样做:

  1. 把pkcs# 12文件放到我的应用包中
  2. 将此文件读取为NSDatapkcsData
  3. 使用SecPKCS12Import方法导入pkcsData
  4. 从上面导入的数据中获取身份和证书,并生成证书数组
  5. 将数组设置为CFWriteStreamRef
  6. kCFStreamPropertySSLSettings中的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

相关内容

  • 没有找到相关文章