我们有一个WebSocket安全服务器在SSL上工作。我们希望在我们的iOS客户端中放一个客户端SSL证书,以确保与服务器通信时的安全性。
因为我们使用WebSocket,所以在iOS客户端中,我们使用SocketRocket(Objective-C WebSocket客户端库)来实现WebSocket通信。
问题是我不知道如何发送我的客户端SSL证书到服务器。
我可以设置CFStream的属性,如kCFStreamPropertySocketSecurityLevel
。但我不知道它是怎么运作的。我在CFStream中找不到任何关于证书的文档。
我知道当我们需要连接到HTTPS服务器时,我们可以在NSURLConnection中使用didReceiveAuthenticationChallenge
。但是据我所知,在CFStream中没有对应的。
谁有什么主意?
经过大量的研究和尝试,我现在可以自己回答了。也希望对你有帮助。
实际上,我需要的是通过使用CFStream实现客户端SSL认证。所以我需要这样做:
- 把pkcs# 12文件放到我的应用包中
- 将此文件读取为
NSData
到pkcsData
- 使用
SecPKCS12Import
方法导入pkcsData
- 从上面导入的数据中获取身份和证书,并生成证书数组
- 将数组设置为
CFWriteStreamRef
的
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