GCDAsynSocket
GCDAsynSocket
是一个开源的基于GCD的异步的socket库。它支持IPV4和IPV6地址,SSL/TLS协议。同时它支持iOS端和Mac端。本篇主要介绍一下 GCDAsynSocket 中的 SSL/TLS 双向手动验证的实现
证书
p12证书:
// 通过 pem 证书转换而来 openssl pkcs12 -export -in my_cer.pem -out my_cer.p12
cer证书:
/*
// 方法一:
// 服务器自签名证书:
// openssl req -new -x509 -nodes -days 365 -newkey rsa:1024 -out my_cer.crt -keyout my_cer.key
// 需要cer格式证书,分发到终端后需要转换一下
// openssl x509 -outform der -in my_cer.crt -out my_cer.der
// 后缀名改成 cer 即可
//方法二:
// 1.获取根证书p12文件
// 2.导入钥匙串
// 3.从钥匙串导出根证书cer文件即可
*/
SSL/TLS
- (void)addSecurtyTransport {
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
// SSL 证书
NSData *pkcs12data = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"my_gateway" ofType:@"p12"]];
CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(pkcs12data);
// c语言字符串
CFStringRef password = CFSTR("123456");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
CFRelease(options);
CFRelease(password);
if(securityError == errSecSuccess)
ZQLog(@"Success opening p12 certificate.");
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef myIdent = (SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
SecIdentityRef certArray[1] = { myIdent };
CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
//开始手动SSL证书验证,必定要设置此key
[settings setObject:(id)CFBridgingRelease(myCerts) forKey:(NSString *)kCFStreamSSLCertificates];
[settings setObject:@(YES) forKey:GCDAsyncSocketManuallyEvaluateTrust];
[settings setObject:@(NO) forKey:GCDAsyncSocketUseCFStreamForTLS];
[self.socketClient startTLS:settings];
[self.socketClient readDataWithTimeout:-1 tag:0];
}
#progma mark - Delegate
// # socket已连接,开启SSL
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
if (self.useTLS) {
[self addSecurtyTransport];
}
}
// # SSL连接已建立
- (void)socketDidSecure:(GCDAsyncSocket *)sock {
if (self.useTLS) {
_socketClient = sock;
}
}
// # 手动验证服务端证书
- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL))completionHandler {
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"my_gateway" ofType:@"cer"];
NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
OSStatus status = -1;
SecTrustResultType result = kSecTrustResultDeny;
if (cerData) {
SecCertificateRef cert1;
// 将DER encoded X.509转换成 SecCertificateRef
cert1 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) cerData);
NSArray *caArray = [NSArray arrayWithObjects:(__bridge id)(cert1), nil];
// 设置证书用于验证
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)caArray);
// 同步验证服务器证书和本地证书是否匹配,会一直阻塞验证
status = SecTrustEvaluate(trust, &result);
CFRelease(cert1);
} else {
NSLog(@"local certificate could't be loaded!");
completionHandler(NO);
}
if ((status == noErr &&
(result == kSecTrustResultProceed || result == kSecTrustResultUnspecified))) {
NSLog(@"成功通过验证,证书可信");
completionHandler(YES);
} else {
CFArrayRef arrayRefTrust = SecTrustCopyProperties(trust);
NSLog(@"error in connection occured\n %@", arrayRefTrust);
completionHandler(NO);
}
}
自签名证书
// 自签名证书需要设置
[settings setObject:@(YES) forKey:GCDAsyncSocketUseCFStreamForTLS];