iOS-以编程方式信任CA(证书颁发机构)
我正在尝试使用一个应用程序,该应用程序通过套接字/流安全地从iPhone客户端连接到TLS服务器,用于一般数据交换。为此,我使用mackeychain工具建立了自己的CA,并将证书包含在代码包中 现在,我的应用程序应该信任该CA颁发的任何服务器证书。(我不在乎其他应用程序如何处理这些证书,我假设他们不会信任它,因为沙箱。) 我在网上发现了几个类似的问题,但似乎出了点问题 如果我将CA证书拖放到模拟器中并手动接受以信任它,那么与服务器的连接似乎工作正常 但是,当我尝试以编程方式为CA证书建立信任时,我稍后与服务器的连接尝试被拒绝,尽管下面的代码不会生成错误 因此,我一定是把证书实现部分搞错了。。。有什么想法吗 非常感谢iOS-以编程方式信任CA(证书颁发机构),ios,ssl,certificate,programmatically-created,Ios,Ssl,Certificate,Programmatically Created,我正在尝试使用一个应用程序,该应用程序通过套接字/流安全地从iPhone客户端连接到TLS服务器,用于一般数据交换。为此,我使用mackeychain工具建立了自己的CA,并将证书包含在代码包中 现在,我的应用程序应该信任该CA颁发的任何服务器证书。(我不在乎其他应用程序如何处理这些证书,我假设他们不会信任它,因为沙箱。) 我在网上发现了几个类似的问题,但似乎出了点问题 如果我将CA证书拖放到模拟器中并手动接受以信任它,那么与服务器的连接似乎工作正常 但是,当我尝试以编程方式为CA证书建立信任时
NSString* certPath = [[NSBundle mainBundle] pathForResource:@"MyTestCA2" ofType:@"cer"]; //cer = CA certificate
NSData* certData = [NSData dataWithContentsOfFile:certPath];
SecCertificateRef cert;
if( [certData length] ) {
cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
if( cert != NULL ) {
CFStringRef certSummary = SecCertificateCopySubjectSummary(cert);
NSString* summaryString = [[NSString alloc] initWithString:(__bridge NSString*)certSummary];
NSLog(@"CERT SUMMARY: %@", summaryString);
certSummary = nil;
} else {
NSLog(@" *** ERROR *** trying to create the SSL certificate from data located at %@, but failed", certPath);
}
}
OSStatus err = noErr;
CFTypeRef result;
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kSecClassCertificate, kSecClass,
cert, kSecValueRef,
nil];
err = SecItemAdd((__bridge CFDictionaryRef)dict, &result);
if(err!=noErr) NSLog(@"error while importing");
if (err==errSecDuplicateItem) NSLog(@"Cert already installed");
NSLog(@":%i",(int)err);
assert(err==noErr||err==errSecDuplicateItem); // accept no errors other than duplicate
err = noErr;
SecTrustRef trust;
err = SecTrustCreateWithCertificates(cert, SecPolicyCreateBasicX509() ,&trust);
assert(err==noErr);
err = noErr;
CFMutableArrayRef newAnchorArray = CFArrayCreateMutable(kCFAllocatorDefault,0,&kCFTypeArrayCallBacks);
CFArrayAppendValue(newAnchorArray,cert);
err = SecTrustSetAnchorCertificates(trust, newAnchorArray);
assert(err==noErr);
SecTrustResultType trustResult;
err=SecTrustEvaluate(trust,&trustResult);
assert(err==noErr);
cert=nil;
我没有尝试运行部分代码,但我知道有一些代码(如下提供)可以运行。我用它来信任我的内部CA
err=SecTrustEvaluate(trust,&trustResult);
assert(err==noErr);
trustResult
是您感兴趣的内容,而不是从SecTrustEvaluate
返回的err
err
告诉您API调用是否成功/失败;它不会告诉您信任评估的结果
我认为你有两种策略。首先是在trustResult
中查找值为kSecTrustResultProceed
或kSecTrustResultUnspecified
的“success”。它之所以“成功”,是因为它不“及时”,不“试图恢复”,也不“失败”
第二种策略是trustResult
中的“不失败”,其值为ksectrustsresultdeny
,ksectrustsresultfailure
或ksectrustsresulttothererror
。也就是说,只要trustResult
不是这些值中的一个,那么就作为成功进行。忽略提示用户信任证书,因为他们无法理解提示并“点击”
下面是我在代表的-didReceiveAuthenticationChallenge:
中使用的代码。它需要一个ASN.1/DER编码的证书(名为ca-cert.DER
)。它使用上述策略1。如果使用#ifdef 0
中的代码,则其使用策略2
我认为苹果的、苹果的技术笔记TN2232和苹果的技术问答QA1360可能对你有用
尽管做出了种种努力,但我仍然无法用NS/CF流复制jww使用NSURLConnection管理的内容。(与之前一样,在模拟器上拖放CA证书并将其作为受信任的证书接受时,TLS流工作正常,但以编程方式执行不会成功。) 因此,我在这个主题上做了更多的搜索,实际上找到了aeternusrahl的另一篇博文,由Rob Napier回复: …其中引用了Heath Borders的以下博客条目: 对于所有和我有类似问题的人,我可以非常推荐这些链接——它们提供了很好的洞察力 没有复制这些链接的全部内容:Rob Napier说,“……据我所知,钥匙链中只有一个受信任的锚列表,每个人都共享。这对socket.io帮助不大,因为它不允许您访问其NSURLConnection委托方法。您必须修改socket.io以接受信任锚定。” 由于以上所有作者都有着杰出的声誉,而且我对iOS编程还很陌生,所以我不敢反对任何一个!但由于我在整合这些帖子时遇到了困难,我非常希望能够澄清我可能无意中跳过的帖子中的前提条件 对于我来说,JWW在使用自己的根证书时不考虑禁用自动TLS验证(见他对我的第一篇文章的评论的回答)。然后,Heath Borders/Rob Napier似乎建议对套接字禁用ssl设置中的证书链验证是必要的(如果我没有弄错的话) 基本上,我看到以下可能的解释: A) jww仅指NSURLConnections,它的委托似乎比您在使用NS/CF流时遇到的委托更强大 B) 自从Rob Napier的帖子/Heath Borders的博客发布以来,情况已经发生了变化 C) 我把一切都搞错了,在这种情况下我提前道歉 虽然A)似乎更有可能,但我还是希望B) 我将非常感谢您提供更多的见解
另外,我希望把以上作为一个答案不违反任何规则。。。这肯定不是正确/完整的答案,但开始一个全新的问题似乎没有用,不幸的是,文本太长,无法发表任何评论。如果有更好的方法在列出仍不清楚的点的同时包含其他信息(如上面的新链接),请告诉我。我得到kSecResultUnspecified。由于系统似乎对此结果不满意,我是否需要完全禁用自动TLS验证,而不是手动检查SecTrustEvaluate,然后如果我得到kSecResultUnspecified,是否继续?因为我需要在服务器和客户端之间进行灵活的通信,所以我认为我需要使用NS/CF流而不是NSURLConnections。除了stream:handleEvent:我不知道NS/CF流有代理,是吗?有没有任何方法可以让我实现ksecresultproceduce而不必默认信任,也不必要求用户接受我的根CA?@MacMini-请参阅苹果的技术问答QA1360。或者查看上面源代码中的注释。当系统返回
kSecResultUnspecified
时,它不会不高兴。感谢您快速而详细的回答,代码肯定会很有价值!在你对我的评论的回复中,你说:系统与'kSecT'配合良好
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:
(NSURLAuthenticationChallenge *)challenge
{
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust]
forAuthenticationChallenge: challenge];
if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust])
{
do
{
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
NSCAssert(serverTrust != nil, @"serverTrust is nil");
if(nil == serverTrust)
break; /* failed */
NSData* caCert = [NSData dataWithContentsOfFile:@"ca-cert.der"];
NSCAssert(caCert != nil, @"caCert is nil");
if(nil == caCert)
break; /* failed */
SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
NSCAssert(caRef != nil, @"caRef is nil");
if(nil == caRef)
break; /* failed */
NSArray* caArray = [NSArray arrayWithObject:(__bridge id)(caRef)];
NSCAssert(caArray != nil, @"caArray is nil");
if(nil == caArray)
break; /* failed */
OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");
if(!(errSecSuccess == status))
break; /* failed */
SecTrustResultType result = -1;
status = SecTrustEvaluate(serverTrust, &result);
if(!(errSecSuccess == status))
break; /* failed */
NSLog(@"Result: %d", result);
/* https://developer.apple.com/library/ios/technotes/tn2232/_index.html */
/* https://developer.apple.com/library/mac/qa/qa1360/_index.html */
/* kSecTrustResultUnspecified and kSecTrustResultProceed are success */
if(result != kSecTrustResultUnspecified && result != kSecTrustResultProceed)
break; /* failed */
#if 0
/* Treat kSecTrustResultConfirm and kSecTrustResultRecoverableTrustFailure as success */
/* since the user will likely tap-through to see the dancing bunnies */
if(result == kSecTrustResultDeny || result == kSecTrustResultFatalTrustFailure || result == kSecTrustResultOtherError)
break; /* failed to trust cert (good in this case) */
#endif
// The only good exit point
return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust]
forAuthenticationChallenge: challenge];
} while(0);
}
// Bad dog
return [[challenge sender] cancelAuthenticationChallenge: challenge];
}