Objective-C:在TLS TCP连接上生成由我们自己的PKI(根CA)签名的服务器证书 [英] Objective-C: eveluate server certificate signed by our own PKI (root CA) on TLS TCP connection
问题描述
* 已解决 *
我的问题是引用以下问题:
Objective-C:如何使用签名者的公钥验证SecCertificateRef?
My problem is referencing to the following question:
Objective-C: How to verify SecCertificateRef with signer's public key?
我们拥有自己的PKI,因此我们信任自己的rootCA。使用此rootCA,我们将签署提供给个人服务器的证书。现在我想连接iOS应用程序并检查从服务器发送的证书是否已通过我们的CA签名。
We have an own PKI and so an own rootCA that we trust. With this rootCA we sign the certificates that are delivered to the personal servers. Now I want to connect with the iOS app and check if the cert that is delivered from the server is signed with our CA.
我的应用程序应该能够使用由 GCDAsyncSocket
建立的TCP连接连接到具有此证书的n个服务器(可能使用零配置服务找到)。我在我的应用程序中有公共部分证书,我想添加到我的CertChain,以便应用程序在连接时信任它们。
My app should be able to connect to n servers with this certificates (maybe found with zero-conf service) using a TCP-connection, established by GCDAsyncSocket
. I have the public part of the certificate in my app that I would like to add to my "CertChain" so the app will trust them on connect.
我试过了很多,但我仍然无法通过 SecTrustEvaluate(trust,& result);
获得有效结果。
(我想在高效中使用它,所以请不要告诉我有关停用验证的任何信息)
I have tried a lot, but I'm still not able to pass SecTrustEvaluate(trust, &result);
with a valid result.
(I want to use this in productive, so please don't tell me anything about deactivating validation)
我的证书:
app中的
:rootCA,oldServerCA(cer)
服务器上的
(通过信任):homeServer,oldServer
My certificates:
in app: rootCA, oldServerCA (cer)
on server (via trust): homeServer, oldServer
我的证书链:
rootCA签名homeServer
oldServerCA签署oldServer
My certificate chain:
rootCA signed homeServer
oldServerCA signed oldServer
我的代码部分:
添加了更新
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;
{
// Configure SSL/TLS settings
NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:3];
// Allow self-signed certificates
[settings setObject:[NSNumber numberWithBool:YES]
forKey:GCDAsyncSocketManuallyEvaluateTrust];
[sock startTLS:settings];
// get the certificates as data for further operations
NSString *certFilePath1 = [[NSBundle mainBundle] pathForResource:@"rootCA" ofType:@"cer"]; // also tried it with 'der', same result
NSData *certData1 = [NSData dataWithContentsOfFile:certFilePath1];
NSString *certFilePath2 = [[NSBundle mainBundle] pathForResource:@"oldServerCA" ofType:@"cer"];
NSData *certData2 = [NSData dataWithContentsOfFile:certFilePath2];
// if data exists, use it
if(certData1 && certData2)
{
SecCertificateRef cert1;
cert1 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData1);
SecCertificateRef cert2;
cert2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData2);
// only working for "cer"
NSString *name = [NSString stringWithUTF8String:CFStringGetCStringPtr(SecCertificateCopySubjectSummary(cert1), kCFStringEncodingUTF8)];
// maybe I understood the usage of "name" in "kSecAttrApplicationTag" wrong?
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)(kSecClassKey), kSecClass,
(__bridge id)kSecAttrKeyTypeRSA, kSecAttrKeyType,
(__bridge id)kSecAttrKeyClassPublic, kSecAttrKeyClass,
kCFBooleanTrue, kSecAttrIsPermanent,
[name dataUsingEncoding:NSUTF8StringEncoding], kSecAttrApplicationTag,
certData1, kSecValueData,
kCFBooleanTrue, kSecReturnPersistentRef,
nil],
NULL); //don't need public key ref
// Setting "cer" is successfully and delivers "noErr" in first run, then "errKCDuplicateItem"
NSLog(@"evaluate with status %d", (int)status);
NSString *name2 = [NSString stringWithUTF8String:CFStringGetCStringPtr(SecCertificateCopySubjectSummary(cert2), kCFStringEncodingUTF8)];
OSStatus status2 = SecItemAdd((__bridge CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)(kSecClassKey), kSecClass,
(__bridge id)kSecAttrKeyTypeRSA, kSecAttrKeyType,
(__bridge id)kSecAttrKeyClassPublic, kSecAttrKeyClass,
kCFBooleanTrue, kSecAttrIsPermanent,
[name2 dataUsingEncoding:NSUTF8StringEncoding], kSecAttrApplicationTag,
certData2, kSecValueData,
kCFBooleanTrue, kSecReturnPersistentRef,
nil],
NULL); //don't need public key ref
NSLog(@"evaluate with status %d", (int)status2);
// log here -> certificates were loaded. Fine
// create references of each to proof them seperatly
const void *ref[] = {cert1};
CFArrayRef aryRef = CFArrayCreate(NULL, ref, 1, NULL);
const void *ref2[] = {cert2};
CFArrayRef aryRef2 = CFArrayCreate(NULL, ref2, 1, NULL);
// need this way to get sock.sslContext, otherways it's NULL (see implementation of GCDAsyncSocket)
[sock performBlock:^{
SSLContextRef sslContext = sock.sslContext;
OSStatus status = SSLSetCertificate(sslContext, aryRef);
// the status is everywhere always -909 -> badReqErr /*bad parameter or invalid state for operation*/
if(status == noErr)
NSLog(@"successfully set ssl certificates");
else
NSLog(@"setting ssl certificates failed");
status = SSLSetCertificate(sock.sslContext, aryRef2);
if(status == noErr)
NSLog(@"successfully set ssl certificates");
else
NSLog(@"setting ssl certificates failed");
status = SSLSetEncryptionCertificate(sock.sslContext, aryRef);
if(status == noErr)
NSLog(@"successfully set ssl certificates");
else
NSLog(@"setting ssl certificates failed");
}];
}
@synchronized( self )
{
if( isConnected == NO )
{
if(gcdAsyncSocket && [gcdAsyncSocket isConnected])
{
isConnected = YES;
[gcdAsyncSocket readDataWithTimeout:READ_TIMEOUT tag:0];
[NSThread detachNewThreadSelector:@selector(readDataToData:withTimeout:tag:) toTarget:gcdAsyncSocket withObject:nil];
[gcdAsyncSocket readDataToData:[GCDAsyncSocket LFData] withTimeout:READ_TIMEOUT tag:0];
[del onConnect];
}
}
}
}
well ...如果不在这里工作,那么手动检查...
well... if not working here, then check manually...
- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust
completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler
{
// https://code.csdn.net/OS_Mirror/CocoaAsyncSocket/file_diff/a4b9c4981b3c022ca89d0cdaadecc70b825ad4f1...5d58af30d2d8a3e0f7219487e72f1b4b2c3b4894/GCD/Xcode/SimpleHTTPClient/Desktop/SimpleHTTPClient/SimpleHTTPClientAppDelegate.m
dispatch_queue_t bgQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(bgQueue, ^{
// This is where you would (eventually) invoke SecTrustEvaluate.
// Presumably, if you're using manual trust evaluation, you're likely doing extra stuff here.
// For example, allowing a specific self-signed certificate that is known to the app.
NSString *certFilePath1 = [[NSBundle mainBundle] pathForResource:@"rootCA" ofType:@"cer"];
NSData *certData1 = [NSData dataWithContentsOfFile:certFilePath1];
NSString *certFilePath2 = [[NSBundle mainBundle] pathForResource:@"oldServerCA" ofType:@"cer"];
NSData *certData2 = [NSData dataWithContentsOfFile:certFilePath2];
if(certData1 && certData2)
{
CFArrayRef arrayRefTrust = SecTrustCopyProperties(trust);
SecTrustResultType result = kSecTrustResultUnspecified;
// usualy should work already here
OSStatus status = SecTrustEvaluate(trust, &result);
NSLog(@"evaluate with result %d and status %d", result, (int)status);
NSLog(@"trust properties: %@", arrayRefTrust);
/* log:
evaluate with result 5 and status 0
trust properties: (
{
type = error;
value = "Root certificate is not trusted."; // expected, when top part was not working
}
*/
SecCertificateRef cert1;
cert1 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData1);
SecCertificateRef cert2;
cert2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData2);
const void *ref[] = {cert1};
CFIndex count = SecTrustGetCertificateCount(trust);
// CFMutableArrayRef aryRef = CFArrayCreateMutable(NULL, count + 1, NULL);
// CFArrayAppendValue(aryRef, ref);
CFArrayCreate(NULL, ref, 2, NULL);
// # # # #
// so check one by one...
BOOL isMatching = NO;
for (int i = 0; i < count; i++)
{
SecCertificateRef certRef = SecTrustGetCertificateAtIndex(trust, i);
NSString *name = [NSString stringWithUTF8String:CFStringGetCStringPtr(SecCertificateCopySubjectSummary(certRef), kCFStringEncodingUTF8)]; // only working for "cer"
NSLog(@"remote cert at index %d is '%@'", i, name);
/*
first is 'homeserver', second is 'oldServer'
*/
// const void *ref[] = {certRef, cert1, cert2};
// CFArrayRef aryCheck = CFArrayCreate(NULL, ref, 3, NULL);
// check against the new cert (rootCA)
const void *ref[] = {certRef, cert1};
CFArrayRef aryCheck = CFArrayCreate(NULL, ref, 2, NULL);
SecTrustRef trustManual;
OSStatus certStatus = SecTrustCreateWithCertificates(aryCheck, SecPolicyCreateBasicX509(), &trustManual);
// certStatus always noErr
NSLog(@"certStatus: %d", (int)certStatus);
SecTrustResultType result;
OSStatus status = SecTrustEvaluate(trustManual, &result);
CFArrayRef arrayRef = SecTrustCopyProperties(trustManual);
NSLog(@"evaluate with result %d and status %d", result, (int)status);
NSLog(@"trust properties: %@", arrayRef);
/* log:
evaluate with result 5 and status 0
trust properties: (
{
type = error;
value = "Root certificate is not trusted.";
}
*/
// always else-part because result is "kSecTrustResultRecoverableTrustFailure"
if (status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified))
{
isMatching = YES;
NSLog(@"certificates matches");
}
else
{
NSLog(@"certificates differs");
}
}
if (isMatching || (status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)))
{
completionHandler(YES);
}
else
{
completionHandler(NO);
}
}
completionHandler(NO);
});
}
更新1
已删除
[settings setObject:[NSNumber numberWithBool:YES]
forKey:GCDAsyncSocketManuallyEvaluateTrust];
立即使用
SecCertificateRef cert1, cert2;
// init certs, see top part
// according to @SeanBaker "Certs[0] would be nil (you don't want to do client auth), and certs[1...] would be the root certificates you want to trust in establishing the connection"
const void *certs[] = {NULL, cert1, cert2};
// const void *certs[] = {nil, cert1, cert2};
CFArrayRef aryCerts = CFArrayCreate(NULL, certs, 3, NULL);
[settings setObject:(__bridge NSArray*)aryCerts
forKey:(NSString *)kCFStreamSSLCertificates];
但获取OSStatus -50(用户参数列表中的 / *错误* /
)
but getting OSStatus -50 (/*error in user parameter list*/
) in
// 2. kCFStreamSSLCertificates
value = [tlsSettings objectForKey:(NSString *)kCFStreamSSLCertificates];
if ([value isKindOfClass:[NSArray class]])
{
CFArrayRef certs = (__bridge CFArrayRef)value;
status = SSLSetCertificate(sslContext, certs);
...
好像我用错了,但我不知道看到错误:/(不经常使用核心基础)
seems like I'm using it wrong, but I don't see the mistake :/ (not using often core foundation)
如果您需要更多信息,请询问。每个提示都可以拯救生命:)
If you need further information, just ask. Every hint can rescue lifes :)
推荐答案
通过手动检查将证书设置为信任的anchorCertificates来解决问题 - (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust
但感谢您的提示和努力:)会给你一些赏金。
completionHandler:(void(^)(BOOL shouldTrustPeer))completionHandler
Solved the problem by setting the certificates as anchorCertificates of the trust in the manual check - (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust
completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler
but thanks for your hints and effort :) will give you some bounty for this.
NSString *certFilePath1 = [[NSBundle mainBundle] pathForResource:@"rootCA" ofType:@"cer"];
NSData *certData1 = [NSData dataWithContentsOfFile:certFilePath1];
NSString *certFilePath2 = [[NSBundle mainBundle] pathForResource:@"oldServerCA" ofType:@"cer"];
NSData *certData2 = [NSData dataWithContentsOfFile:certFilePath2];
OSStatus status = -1;
SecTrustResultType result = kSecTrustResultDeny;
if(certData1 && certData2)
{
SecCertificateRef cert1;
cert1 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData1);
SecCertificateRef cert2;
cert2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData2);
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);
}
这篇关于Objective-C:在TLS TCP连接上生成由我们自己的PKI(根CA)签名的服务器证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!