这是我使用自签名证书进行身份验证的非常标准的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 cert主题名称与我正在连接的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上的这个特定错误?
答案 0 :(得分:10)
您需要使用实际主机名创建特殊策略,然后从中创建并评估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
}
P.S。你没有使用你创建的政策。
我刚刚找到了更完整的解释here。
答案 1 :(得分:1)
对于这个特定问题,您需要阅读 “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