【问题标题】:kSecTrustResultRecoverableTrustFailure when connecting to https with self-signed certificate using NSURLConnection使用 NSURLConnection 使用自签名证书连接到 https 时的 kSecTrustResultRecoverableTrustFailure
【发布时间】:2014-08-05 11:10:30
【问题描述】:

我在这里看到了一些问题,但没有一个对我有帮助。人们解决的问题主要是重新生成服务器证书: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。

我做错了什么?谢谢。

更新:好的,我发现原因是“服务器的证书与 URL 不匹配”。

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

【问题讨论】:

    标签: ios objective-c ssl https openssl


    【解决方案1】:

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

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

    简而言之:在您第一次遇到证书时合理地验证它。您仍然可以使用大多数传统的 PKI/PKIX 测试。一旦证书通过了所有测试(“受信任的根路径”除外),您就将其称为“受信任”。这种策略称为首次使用信任或 TOFU。

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


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

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

    您需要让用户脱离循环,因为他们总是会做出让他们尽快通过消息框的决定。不管他们回答对错都无所谓——他们想看跳舞的兔子。

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


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

    这违背了 PKI{X} 的目的。如果您愿意接受任何主机、任何公钥或任何签名,为什么还要首先使用 PKI{X}? PKI{X} 中 X509 的全部意义在于使用受信任的第三方签名(在本例中为自签名)将实体或主机绑定到公钥。

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

    【讨论】:

    • 好的,现在我决定将服务器证书与我的本地副本进行比较,并检查主机名的 IP 地址是否与我的本地副本匹配。希望我能找到如何检索服务器证书和 IP 地址的工作示例 =/
    • 这是个好策略。它称为证书固定。 IP 块效果很好。您不希望有一天看到美国或英国的 IP 地址,然后是俄罗斯的 IP。可能正在发生一些可疑的事情......
    猜你喜欢
    • 1970-01-01
    • 2012-12-31
    • 1970-01-01
    • 2012-12-07
    • 2014-05-16
    • 1970-01-01
    • 1970-01-01
    • 2023-03-02
    • 1970-01-01
    相关资源
    最近更新 更多