带客户端身份验证的 GCDAsyncSocket
GCDAsyncSocket with Client Authentication
我一直在使用 CocoaAsyncSocket 在没有 SSL 的情况下向我们的服务器发送数据。现在服务器端已经实现了 SSL/TLS 和客户端认证。为了在我们的应用程序中实现这一点,我获得了以下三个文件:
- ca-chain.cert.pem
- 客户端-test.cert.pem
- 客户端-test.key.pem
我将文件转换为 iOS 可读格式,如下所示:
- ca-chain.cert.pem 到 ca-cert.cer
- 客户-test.cert.pem到客户_cert.cer
- 客户端-test.key.pem 到 client_key.p12
我已经让它工作到 SSL。但在客户端身份验证方面遇到问题。
这是我的代码:
didConnectToHost:
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;
{
// Configure SSL/TLS settings
NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:3];
// Allow self-signed certificates
CFArrayRef certsArray = [self loadCertificates];
[settings setObject:@0 forKey:GCDAsyncSocketSSLProtocolVersionMax];
[settings setObject:[NSNumber numberWithBool:YES] forKey:GCDAsyncSocketManuallyEvaluateTrust];
[settings setObject:(id)CFBridgingRelease(certsArray) forKey:GCDAsyncSocketSSLCertificates];
[sock startTLS:settings];
}
didReceiveTrust:
- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler {
NSString *caCertPath = [[NSBundle mainBundle] pathForResource:@"ca-cert" ofType:@"cer"];
NSData *caCertData = [NSData dataWithContentsOfFile:caCertPath];
NSString *clientCertPath = [[NSBundle mainBundle] pathForResource:@"client_cert" ofType:@"cer"];
NSData *clientCertData = [NSData dataWithContentsOfFile:clientCertPath];
OSStatus status = -1;
SecTrustResultType result = kSecTrustResultDeny;
if(caCertData && clientCertData)
{
SecCertificateRef cert1;
cert1 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) caCertData);
SecCertificateRef cert2;
cert2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) clientCertData);
const void *ref[] = {cert1,cert2};
CFArrayRef ary = CFArrayCreate(NULL, ref, 2, NULL);
SecTrustSetAnchorCertificates(trust, ary);
status = SecTrustEvaluate(trust, &result);
}
else
{
NSLog(@"local certificates could not be loaded");
completionHandler(NO);
}
if ((status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)))
{
completionHandler(YES);
}
else
{
CFArrayRef arrayRefTrust = SecTrustCopyProperties(trust);
NSLog(@"error in connection occured\n%@", arrayRefTrust);
completionHandler(NO);
}
}
加载证书:
-(CFArrayRef) loadCertificates
{
NSString *clientKeyPath = [[NSBundle mainBundle] pathForResource:@"client_key" ofType:@"p12"];
NSData* clientKeyData = [NSData dataWithContentsOfFile:clientKeyPath];
NSLog(@"key : %@",[[NSString alloc] initWithData:clientKeyData encoding:NSASCIIStringEncoding]);
CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(clientKeyData);
CFStringRef password = CFSTR("_mypassword_");
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)
NSLog(@"Success opening p12 certificate.");
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef myIdent = (SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
NSString *clientCertPath = [[NSBundle mainBundle] pathForResource:@"client_cert" ofType:@"cer"];
NSData *clientCertData = [NSData dataWithContentsOfFile:clientCertPath];
SecCertificateRef clientCert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) clientCertData);
//SecIdentityRef certArray[1] = { myIdent };
const void *ref[] = {myIdent, clientCert};
CFArrayRef myCerts = CFArrayCreate(NULL, ref, 2, NULL);
// NSString *caCertPath = [[NSBundle mainBundle] pathForResource:@"ca-cert" ofType:@"cer"];
// NSData *caCertData = [NSData dataWithContentsOfFile:caCertPath];
//
// SecCertificateRef caCert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) caCertData);
//
// const void *ref[] = {clientCert, myIdent, caCert};
// CFArrayRef myCerts = CFArrayCreate(NULL, ref, 3, NULL);
return myCerts;
}
这两天我一直在为客户端身份验证而苦恼。我现在收到此错误:
2017-03-13 15:35:40.777 MPS[79612:1478858] GCDAsyncSocket socketDidDisconnect Error - Error Domain=kCFStreamErrorDomainSSL Code=-9806 "(null)" UserInfo={NSLocalizedRecoverySuggestion=Error code definition can be found in Apple's SecureTransport.h}
我读到 CocoaAyncSocket 库在使用手动信任评估的客户端身份验证方面存在问题。我尝试了此处提到的解决方法:Support for client side authentication with manual trust evaluation 还是不行。我找不到我丢失的东西。
提前致谢!
-乌玛
代码一切正常。我希望这可以帮助寻找答案的人。然而,问题在于文件转换。当我使用别人转换后的文件时,它起作用了。
我一直在使用 CocoaAsyncSocket 在没有 SSL 的情况下向我们的服务器发送数据。现在服务器端已经实现了 SSL/TLS 和客户端认证。为了在我们的应用程序中实现这一点,我获得了以下三个文件:
- ca-chain.cert.pem
- 客户端-test.cert.pem
- 客户端-test.key.pem
我将文件转换为 iOS 可读格式,如下所示:
- ca-chain.cert.pem 到 ca-cert.cer
- 客户-test.cert.pem到客户_cert.cer
- 客户端-test.key.pem 到 client_key.p12
我已经让它工作到 SSL。但在客户端身份验证方面遇到问题。
这是我的代码:
didConnectToHost:
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;
{
// Configure SSL/TLS settings
NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:3];
// Allow self-signed certificates
CFArrayRef certsArray = [self loadCertificates];
[settings setObject:@0 forKey:GCDAsyncSocketSSLProtocolVersionMax];
[settings setObject:[NSNumber numberWithBool:YES] forKey:GCDAsyncSocketManuallyEvaluateTrust];
[settings setObject:(id)CFBridgingRelease(certsArray) forKey:GCDAsyncSocketSSLCertificates];
[sock startTLS:settings];
}
didReceiveTrust:
- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler {
NSString *caCertPath = [[NSBundle mainBundle] pathForResource:@"ca-cert" ofType:@"cer"];
NSData *caCertData = [NSData dataWithContentsOfFile:caCertPath];
NSString *clientCertPath = [[NSBundle mainBundle] pathForResource:@"client_cert" ofType:@"cer"];
NSData *clientCertData = [NSData dataWithContentsOfFile:clientCertPath];
OSStatus status = -1;
SecTrustResultType result = kSecTrustResultDeny;
if(caCertData && clientCertData)
{
SecCertificateRef cert1;
cert1 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) caCertData);
SecCertificateRef cert2;
cert2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) clientCertData);
const void *ref[] = {cert1,cert2};
CFArrayRef ary = CFArrayCreate(NULL, ref, 2, NULL);
SecTrustSetAnchorCertificates(trust, ary);
status = SecTrustEvaluate(trust, &result);
}
else
{
NSLog(@"local certificates could not be loaded");
completionHandler(NO);
}
if ((status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)))
{
completionHandler(YES);
}
else
{
CFArrayRef arrayRefTrust = SecTrustCopyProperties(trust);
NSLog(@"error in connection occured\n%@", arrayRefTrust);
completionHandler(NO);
}
}
加载证书:
-(CFArrayRef) loadCertificates
{
NSString *clientKeyPath = [[NSBundle mainBundle] pathForResource:@"client_key" ofType:@"p12"];
NSData* clientKeyData = [NSData dataWithContentsOfFile:clientKeyPath];
NSLog(@"key : %@",[[NSString alloc] initWithData:clientKeyData encoding:NSASCIIStringEncoding]);
CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(clientKeyData);
CFStringRef password = CFSTR("_mypassword_");
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)
NSLog(@"Success opening p12 certificate.");
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef myIdent = (SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
NSString *clientCertPath = [[NSBundle mainBundle] pathForResource:@"client_cert" ofType:@"cer"];
NSData *clientCertData = [NSData dataWithContentsOfFile:clientCertPath];
SecCertificateRef clientCert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) clientCertData);
//SecIdentityRef certArray[1] = { myIdent };
const void *ref[] = {myIdent, clientCert};
CFArrayRef myCerts = CFArrayCreate(NULL, ref, 2, NULL);
// NSString *caCertPath = [[NSBundle mainBundle] pathForResource:@"ca-cert" ofType:@"cer"];
// NSData *caCertData = [NSData dataWithContentsOfFile:caCertPath];
//
// SecCertificateRef caCert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) caCertData);
//
// const void *ref[] = {clientCert, myIdent, caCert};
// CFArrayRef myCerts = CFArrayCreate(NULL, ref, 3, NULL);
return myCerts;
}
这两天我一直在为客户端身份验证而苦恼。我现在收到此错误:
2017-03-13 15:35:40.777 MPS[79612:1478858] GCDAsyncSocket socketDidDisconnect Error - Error Domain=kCFStreamErrorDomainSSL Code=-9806 "(null)" UserInfo={NSLocalizedRecoverySuggestion=Error code definition can be found in Apple's SecureTransport.h}
我读到 CocoaAyncSocket 库在使用手动信任评估的客户端身份验证方面存在问题。我尝试了此处提到的解决方法:Support for client side authentication with manual trust evaluation 还是不行。我找不到我丢失的东西。
提前致谢! -乌玛
代码一切正常。我希望这可以帮助寻找答案的人。然而,问题在于文件转换。当我使用别人转换后的文件时,它起作用了。