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

我对使用SSL通道使用Web服务相当陌生。 经过相当不错的search后,我发现了一种使用NSURLConnection委托API来执行SSL / HTTPS身份validation的方法。 以下是执行实际身份validation的代码片段:

- (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]; } 

}

但是我得到以下错误:

 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} 

我正在使用从服务器获得的相同证书,并将其转换为“der”格式。 我正在构buildiOS 5.x的应用程序。 我不确定我是否错过了某些东西。 让我知道你的build议。

谢谢。

编辑在此处检查证书后,输出如何显示: Portecle应用程序考试

让我知道如果有什么问题。

谢谢。

我无法确定您的代码是否有效,因为我使用RestKit来使用REST接口,但是导致NSURLErrorDomain Code=-1012的最常见问题是自签名证书没有指定subject alternative name扩展名Web服务如果地址。

要检查您的证书,请下载Portecle应用程序 ,如果您需要查看SSL证书,非常有用。 运行它并从菜单中select检查 – >检查证书并导航到您的证书。 您将看到有关您的证书的基本信息,现在按“检查”button,然后select“替代名称”,并确保您的Web服务的正确IP地址在那里。 如果没有,则需要重新创build证书,并附上这些信息。

我弄清楚如何解决这个问题。

我结束了比较客户端和服务器信任证书,逐字节。 虽然可能有另外一种办法来解决这类自签证书的问题,但是对于这个解决scheme确实有用。 下面是我如何使用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]; } 

希望这能帮助那些正在寻找解决类似问题的人,

谢谢。