对于使用者名称不匹配的自签名 CA,SecTrustEvaluate 失败,并显示 kSecTrustResultRec



这是我非常标准的NSURLConnection回调,用于使用自签名证书进行身份验证:

- (SecCertificateRef)certRefFromDerNamed:(NSString*)derFileName resultingDataRef:(CFDataRef*)dataRefPtr{
    NSString *thePath = [[NSBundle mainBundle] pathForResource:derFileName ofType:@"der"];
    NSData *certData = [[NSData alloc] initWithContentsOfFile:thePath];
    CFDataRef certDataRef = (__bridge_retained CFDataRef)certData;
    SecCertificateRef cert = SecCertificateCreateWithData(NULL, certDataRef);
    *dataRefPtr = certDataRef;
    return cert;
}
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if (connection == self.connection) {
    BOOL trusted = NO;
     if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        SecPolicyRef policyRef = SecPolicyCreateBasicX509();
        SecCertificateRef cert1;
        CFDataRef certData1;
        cert1 = [self certRefFromDerNamed:@"some3rdpartycacert" resultingDataRef:&certData1];
        SecCertificateRef certArray[1] = { cert1 };
        CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
        SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
        SecTrustSetAnchorCertificates(serverTrust, certArrayRef);
        SecTrustResultType trustResult;
        SecTrustEvaluate(serverTrust, &trustResult);
        trusted = (trustResult == kSecTrustResultUnspecified);
        CFRelease(certArrayRef);
        CFRelease(policyRef);
        CFRelease(cert1);
        CFRelease(certData1);
    }
    if (trusted) {
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
    } else {
        [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
    }
}
}

trustResult总是kSecTrustResultRecoverableTrustFailure.

证书本身有点问题。根据服务器上的 curl 证书主题名称与我连接到的 url 不匹配。我已经联系了该第三方公司,他们告诉我我需要在我的代码中接受这种 url 不匹配。问题是我不知道如何在iOS上执行此操作。我可以完全绕过证书检查(通过简单地假设trusted=YES并调用useCredential)或完全失败。从安全的角度来看,第一种解决方案显然是错误的,并且容易受到MITM攻击。

这是 CURL 输出(我在这里对同一证书使用了 PEM 版本):

ukaszs-iMac:Preferences lukasz$  curl --verbose --cacert ~/Desktop/some3rdpartycacert.txt  https://dev-service.some3rdparty.com:50101/
* About to connect() to dev-service.some3rdparty.com port 50101 (#0)
*   Trying XXX.XXX.XXX.XXX...
* connected
* Connected to dev-service.some3rdparty.com (XXX.XXX.XXX.XXX) port 50101 (#0)
* successfully set certificate verify locations:
*   CAfile: /Users/lukasz/Desktop/some3rdpartycacert.txt
  CApath: none
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Request CERT (13):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using AES256-SHA
* Server certificate:
*    subject: C=CA; ST=Ontario; O=Some 3rdParty Corporation; CN=otherpage.some3rdparty.com; emailAddress=noc@some3rdparty.com
*    start date: 2013-10-30 16:52:14 GMT
*    expire date: 2013-10-30 16:52:14 GMT
* SSL: certificate subject name 'otherpage.some3rdparty.com' does not match target host name 'dev-service.some3rdparty.com'
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):
curl: (51) SSL: certificate subject name 'otherpage.some3rdparty.com' does not match target host name 'dev-service.some3rdparty.com'

那么,如何在iOS上忽略此特定错误?

您需要

使用实际主机名创建一个特殊策略,然后从中创建和评估serverTrust。大约:

SecPolicyRef policyRef = SecPolicyCreateSSL(true, CFSTR("otherpage.some3rdparty.com"));
OSStatus    status;
SecTrustRef serverTrust;
status = SecTrustCreateWithCertificates(certificatesFromOriginalServerTrust, policyRef, & serverTrust);
// noErr == status?
status = SecTrustSetAnchorCertificates(serverTrust, certArrayRef);
// noErr == status?
SecTrustResultType trustResult;
status = SecTrustEvaluate(serverTrust, &trustResult);
// noErr == status?
if(kSecTrustResultProceed == trustResult || kSecTrustResultUnspecified == trustResult) {
    // all good
}

附言:您没有使用您创建的策略。

我刚刚在这里找到了一个更完整的解释。

对于这个特定的问题,你需要阅读"iOS 5 编程突破极限:为 Apple iPhone、iPad 和 iPod touch 开发非凡的移动应用程序"一书。(作者:Rob Napier, Mugunth Kumar)

在书中,在第219页的底部,它说:"如果您对传递的名称感到满意,则可以将证书重新评估为简单的 X.509 证书,而不是作为 SSL 握手的一部分(也就是说,您在忽略主机名的情况下对其进行评估)"

然后它具有特殊的RNSecTrustEvaluateAsX509功能。因此,实际上使用该函数,您可以忽略主机名部分并将该部分设置为"受信任"来评估证书。请参阅 http://bit.ly/1g1RzdF(转到第 219 页)

书中的代码可以在这里找到:https://github.com/iosptl/ios5ptl/blob/master/ch11/Connection/Connection/ConnectionViewController.m

相关内容

  • 没有找到相关文章

最新更新