使用NSURLConnection连接到具有自签名证书的https时的kSecTrustResultRecoverableTrustFailure

时间:2014-06-15 13:11:52

标签: ios objective-c ssl https openssl

我在这里看到了几个问题,但没有一个问题对我有帮助。人们解决的问题主要是重新生成服务器证书:What is the reason of kSecTrustResultRecoverableTrustFailure?

假设我需要使用自签名证书与服务器建立https连接。我没有来自服务器的任何内部数据,例如其私钥。例如,服务器是https://www.pcwebshop.co.uk/

据我了解,我可以将客户端证书捆绑到应用程序中并使用它进行验证。我是对的,我可以在没有服务器内部数据的情况下获得有效的客户端证书吗?

我在这里搜索了一个教程http://www.indelible.org/ink/trusted-ssl-certificates

以下是我获取客户证书的方式

openssl s_client \
    -showcerts -connect "${HOST}:443" </dev/null 2>/dev/null | \
openssl x509 -outform DER >"../resources/${HOST}.der"

这里是代码(几乎不变):

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    if ([self shouldTrustProtectionSpace:challenge.protectionSpace]) {
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]
             forAuthenticationChallenge:challenge];
    } else {
        [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
    }
}

- (BOOL)shouldTrustProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    // load up the bundled certificate
    NSString *certPath = [[NSBundle mainBundle] pathForResource:protectionSpace.host ofType:@"der"];

    if (certPath == nil)
        return NO;

    OSStatus status;
    NSData *certData = [[NSData alloc] initWithContentsOfFile:certPath];
    CFDataRef certDataRef = (__bridge_retained CFDataRef)certData;
    SecCertificateRef cert = SecCertificateCreateWithData(NULL, certDataRef);

    // establish a chain of trust anchored on our bundled certificate
    CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)&cert, 1, NULL);
    SecTrustRef serverTrust = protectionSpace.serverTrust;
    status = SecTrustSetAnchorCertificates(serverTrust, certArrayRef);
    // status == 0

    // verify that trust
    SecTrustResultType trustResult;
    status = SecTrustEvaluate(serverTrust, &trustResult);
    // status == 0

    CFRelease(certArrayRef);
    CFRelease(cert);
    CFRelease(certDataRef);

    return trustResult == kSecTrustResultUnspecified;
}

trustResult始终是kSecTrustResultRecoverableTrustFailure。

我做错了什么?谢谢。

更新:好的,我发现原因是&#34;服务器的证书与网址不匹配&#34;。

是否可以通过忽略服务器证书的URL(主机名)来解决客户端问题?

1 个答案:

答案 0 :(得分:3)

  

假设我需要使用自签名证书与服务器建立https连接。我没有来自服务器的任何内部数据,例如其私钥。

在这种情况下,您需要一种安全多样化策略。古特曼在他的书Engineering Security中详细介绍了它。

缺点:在您第一次遇到证书时明智地验证证书。您仍然可以使用大多数传统的PKI / PKIX测试。一旦证书通过所有测试(“受信任的根路径”除外),您就将其称为“受信任”。该策略称为Trust On First Use或TOFU。

在后续连接中,您不再需要TOFU,因为您已经遇到过证书或公钥。在后续连接中,您确保证书或公钥是连续的(即,不会更改),IP来自先前遇到的相同区域,等等。如果证书更改,请确保其因为自签名即将到期。警惕意外的变化。


Here's the code (almost unchanged):
...
trustResult == trustResult == kSecTrustResultUnspecified

对于kSecTrustResultUnspecified,请参阅Technical Q&A QA1360。从本质上讲,它是一个可恢复的错误。 Q&amp; A表示提示用户。 Gutmann(和我)说要使用如上所述的安全多元化策略。

您需要让用户退出循环,因为他们总是会尽快做出让他们越过消息框的决定。如果他们回答正确或错误就死了也没关系 - 他们想看到跳舞的兔子。

此外,安全多元化策略甚至适用于kSecTrustResultProceed。考虑一下:DiginotarTrustwave都破坏了PKI {X},而Cocoa / CocoaTouch非常乐意返回kSecTrustResultProceed。它不是Cocoa / CocoaTouch的错 - PKI {X}存在架构缺陷。


  

是否可以通过忽略服务器证书的URL(主机名)来解决客户端问题?

这种做法违背了PKI {X}的目的。如果您接受任何主持人,任何公钥或任何签名,为什么还要首先打扰PKI {X}? PKI {X}中X509的重点是使用受信任的第三方签名(或在本例中为自签名)将实体或主机绑定到公钥。

如果您不关心绑定,只需使用Anonymous Diffie-Hellman并结束安全影院。