简体   繁体   English

iOS 和 SSL:无法验证自签名服务器证书

[英]iOS and SSL: Unable to validate self-signed server certificate


I'm fairly new to consuming webservices using SSL channel.我对使用 SSL 通道使用网络服务还很陌生。 After fairly good search I had found a way to perform SSL/HTTPS authentication using NSURLConnection delegate APIs.经过相当不错的搜索,我找到了一种使用 NSURLConnection 委托 API 执行 SSL/HTTPS 身份验证的方法。 Following is the code snippet that does the actual authentication thing:以下是执行实际身份验证的代码片段:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
[self printLogToConsole:@"Authenticating...."];
[self printLogToConsole:[NSString stringWithFormat:@"\n%@\n", [challenge description]]];
NSLog(@"\n\nserverTrust: %@\n", [[challenge protectionSpace] serverTrust]);

/* Extract the server certificate for trust validation
 */
NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
assert(protectionSpace);
SecTrustRef trust = [protectionSpace serverTrust];    
assert(trust);
CFRetain(trust); // Make sure this thing stays around until we're done with it
NSURLCredential *credential = [NSURLCredential credentialForTrust:trust];


/* On iOS 
 * we need to convert it to 'der' certificate. It can be done easily through Terminal as follows:
 * $ openssl x509 -in certificate.pem -outform der -out rootcert.der
 */
NSString *path = [[NSBundle mainBundle] pathForResource:@"rootcert" ofType:@"der"];
assert(path);
NSData *data = [NSData dataWithContentsOfFile:path];
assert(data);

/* Set up the array of certificates, we will authenticate against and create credentials */
SecCertificateRef rtCertificate = SecCertificateCreateWithData(NULL, CFBridgingRetain(data));
const void *array[1] = { rtCertificate };
trustedCerts = CFArrayCreate(NULL, array, 1, &kCFTypeArrayCallBacks);
CFRelease(rtCertificate); // for completeness, really does not matter

/* Build up the trust anchor using our root cert */
int err;
SecTrustResultType trustResult = 0;
err = SecTrustSetAnchorCertificates(trust, trustedCerts);
if (err == noErr) {
    err = SecTrustEvaluate(trust, &trustResult);
}
CFRelease(trust); // OK, now we're done with it

[self printLogToConsole:[NSString stringWithFormat:@"trustResult: %d\n", trustResult]];

/* http://developer.apple.com/library/mac/#qa/qa1360/_index.html
 */
BOOL trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultConfirm) || (trustResult == kSecTrustResultUnspecified));

// Return based on whether we decided to trust or not
if (trusted) {
    [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    [self printLogToConsole:@"Success! Trust validation successful."];
} else {
    [self printLogToConsole:@"Failed! Trust evaluation failed for service root certificate.\n"];
    [[challenge sender] cancelAuthenticationChallenge:challenge];
}

} }

But I'm getting following error:但我收到以下错误:

2012-06-11 17:10:12.541 SecureLogin[3424:f803] Error during connection: Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x682c790 {NSErrorFailingURLKey=https://staging.esecure.url/authentication/signin/merchants, NSErrorFailingURLStringKey=https://staging.esecure.url/authentication/signin/merchants}


I'm using the same certificate that I got from the server and converted it to 'der' format.我正在使用从服务器获得的相同证书并将其转换为“der”格式。 I'm building app for iOS 5.x.我正在为 iOS 5.x 构建应用程序。 I'm not sure whether I'm missing out on something.我不确定我是否错过了什么。 Let me know of your suggestions.让我知道你的建议。

Thanks.谢谢。

EDIT After examining the certificate here how the output looks:编辑在此处检查证书后输出的外观:Portecle 应用程序考试


Let me know if there is something wrong.如果有什么问题,请告诉我。

Thanks.谢谢。

I cannot tell if your code is valid or not, because I use RestKit for consuming REST interfaces, however the most common problem that results in NSURLErrorDomain Code=-1012 is that the self-signed certificate does not have subject alternative name extension pointing to the web service if address.我不知道,如果你的代码是有效还是无效,因为我用RestKit为使用REST接口,但最常见的问题,导致NSURLErrorDomain Code=-1012是自签名的证书不具有subject alternative name扩展指向网络服务,如果地址。

To examine your certificate, download the Portecle app , very useful if you need to look inside ssl certificates.要检查您的证书,请下载Portecle 应用程序,如果您需要查看 ssl 证书,该应用程序非常有用。 Run it and choose Examine->Examine Certificate from the menu and navigate to your certificate.运行它并从菜单中选择“检查”->“检查证书”并导航到您的证书。 You will see basic information about your certificate, now press the Examine button, then Subject alternative name, and make sure proper ip address of your web service is there.您将看到有关您的证书的基本信息,现在按检查按钮,然后按主题备用名称,并确保您的网络服务的正确 IP 地址在那里。 If not, you need to create the certificate again with this information in place.如果没有,您需要使用此信息再次创建证书。

I did figure out how to resolve this issue.我确实想出了如何解决这个问题。

I ended up comparing the client and server trust certificates, byte-by-byte.我最终逐字节比较了客户端和服务器信任证书。 Although there could be another way to resolve such issues of self-signed certificate, but for this solution did work.虽然可能有另一种方法来解决自签名证书的此类问题,但对于此解决方案确实有效。 Here is how I'm doing comparison of the client and server certificates, byte-by-byte, using their CFData objects(you can also reference 'AdvancedURLConnections' example code provided by Apple):这是我如何使用它们的 CFData 对象逐字节比较客户端和服务器证书(您也可以参考 Apple 提供的“AdvancedURLConnections”示例代码):

success = NO;
        pServerCert = SecTrustGetLeafCertificate(trust);
        if (clientCert != NULL) {
            CFDataRef       clientCertData;
            CFDataRef       serverCertData;

            clientCertData = SecCertificateCopyData(clientCert);
            serverCertData   = SecCertificateCopyData(pServerCert);

            assert(clientCertData != NULL);
            assert(serverCertData   != NULL);

            success = CFEqual(clientCertData, serverCertData);

            CFRelease(clientCertData);
            CFRelease(serverCertData);
        }
        if (success) {
            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
            [self printLogToConsole:@"Success! Trust validation successful."];
        } else {
            [self printLogToConsole:@"Failed! Trust evaluation failed for service root certificate.\n"];
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        }

Hope this will help someone, who is looking for solution of similar issue,希望这会帮助正在寻找类似问题解决方案的人,

Thanks.谢谢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM