在iOS中,如何使用https在服务器上使用自签名证书连接到服务器?

我正在开发的iOS 5,真的不想使用un-ARCed代码,所以我select自己实现而不是使用AFNetworking。 这也可能是一个大问题,所以我把它分成两个小部分。

1)在iOS 5中使用https连接到服务器。我使用从“iOS 5 Programming Pushing the Limits”中提取的代码。 因为我正在为iOS 5开发,所以我不使用我的项目中的弃用方法。 “RNSecTrustEvaluateAsX509”是一种将证书重新评估为简单的X.509证书而不是SSL握手的一部分的方法。

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { NSURLProtectionSpace *protSpace = challenge.protectionSpace; SecTrustRef trust = protSpace.serverTrust; SecTrustResultType result = kSecTrustResultFatalTrustFailure; OSStatus status = SecTrustEvaluate(trust, &result); if (status == errSecSuccess && result == kSecTrustResultRecoverableTrustFailure) { SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, 0); CFStringRef subject = SecCertificateCopySubjectSummary(cert); NSLog(@"Trying to access %@. Got %@.", protSpace.host, (__bridge id)subject); CFRange range = CFStringFind(subject, CFSTR("192.168.1.100"), kCFCompareAnchored|kCFCompareBackwards); if (range.location != kCFNotFound) { NSLog(@"Creating new trust certificate.Ignoring the hostname."); status = RNSecTrustEvaluateAsX509(trust, &result); } CFRelease(subject); } if (status == errSecSuccess) { switch (result) { case kSecTrustResultInvalid: case kSecTrustResultDeny: case kSecTrustResultFatalTrustFailure: case kSecTrustResultOtherError: case kSecTrustResultRecoverableTrustFailure: { NSLog(@"Failing due to result: %lu", result); [challenge.sender cancelAuthenticationChallenge:challenge]; } break; case kSecTrustResultProceed: case kSecTrustResultUnspecified: { NSLog(@"Successing with result: %lu", result); NSURLCredential *cred = [NSURLCredential credentialForTrust:trust]; [challenge.sender useCredential:cred forAuthenticationChallenge:challenge]; } break; default: NSAssert(NO,@"Unexpected result from trust evaluation: %d", result); break; } } else { // Something was broken NSLog(@"Complete failure with code: %lu", status); [challenge.sender cancelAuthenticationChallenge:challenge]; } } 

它连接到服务器,但我总是得到一个错误,说:“该操作无法完成(NSURLErrorDomain错误-1012)”。 而控制台显示“由于结果5失败”,这意味着我得到一个kSecTrustResultRecoverableTrustFailure。 我怀疑这是因为我在服务器上使用自签名证书。 这导致第二个问题如下。

2)自签证书导致问题。 所以我加了这些线

 // Self-signed certificates need to be validated manually. NSArray *anchors = [self serverAnchors]; SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)anchors); SecTrustSetAnchorCertificatesOnly(trust, YES); 

就在之前

 OSStatus status = SecTrustEvaluate(trust, &result); 

在上面的willSendRequestForAuthenticationChallenge方法中。 我也创build了一个方法:

 - (NSArray *)serverAnchors { static NSArray *anchors = nil; if (!anchors) { NSData *caData = [CA_CERTS dataUsingEncoding:NSUTF8StringEncoding]; SecCertificateRef caRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef) caData); anchors = [NSArray arrayWithObjects:(__bridge id)caRef, nil]; if (caRef) { CFRelease(caRef); } } return anchors; } 

我将CA_CERTS定义为“der”格式的证书数据,这是我通过SecCertificateCopyData从服务器获得的NSString。 但我仍然不断得到kSecTrustResultRecoverableTrustFailure。 我真的不知道我是否在这里做正确的事情。 如何使用自己的数据从服务器手动validation自签名证书? 更具体地说,如何从iOS获取数据?

我build议将OpenSSL整合到您的项目中,以处理证书和授权挑战! 那么在你的'连接:'NSURLConnectionDelegate'协议的didReceiveAuthenticationChallenge:'方法做这样的事情:

 - (void) connection:(NSURLConnection*) connection didReceiveAuthenticationChallenge: (NSURLAuthenticationChallenge*) challenge { if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]) { SecTrustRef trust = [[challenge protectionSpace] serverTrust]; NSMutableArray* certificates = [NSMutableArray array]; NSData* certificate2Data = // your certificate data NSData* certificate3Data = // even more certificate data if needed SecCertificateRef certificate2 = SecCertificateCreateWithData(NULL, (CFDataRef) certificate2Data); SecCertificateRef certificate3 = SecCertificateCreateWithData(NULL, (CFDataRef) certificate3Data); [certificates addObject: (id) certificate2]; [certificates addObject: (id) certificate3]; CFRelease(certificate2); CFRelease(certificate3); SecTrustSetAnchorCertificates(trust, (CFArrayRef) certificates); SecTrustSetAnchorCertificatesOnly(trust, true); SecTrustResultType trust_result; SecTrustEvaluate(trust, &trust_result); if (trust_result == kSecTrustResultUnspecified) { if (SecTrustGetCertificateCount(trust) > 0) { SecCertificateRef leafCertificate = SecTrustGetCertificateAtIndex(trust, 0); NSData* leafCertificateData = (NSData*) SecCertificateCopyData(leafCertificate); const unsigned char* certificateDataBytes = (const unsigned char *)[leafCertificateData bytes]; X509* certificateX509 = d2i_X509(NULL, &certificateDataBytes, [leafCertificateData length]); CFRelease(leafCertificateData); X509_NAME *issuerX509Name = X509_get_issuer_name(certificateX509); X509_NAME *subjectX509Name = X509_get_subject_name(certificateX509); /* with issuerX509Name and subjectX509Name you could check some properties of the certificate and cancel the authentication challenge fe! if ([[self valueWithKey: @"CN" inName: subjectX509Name cert: certificateX509] isEqualToString: @"xxxx"] == NO) { [[challenge sender] cancelAuthenticationChallenge: challenge]; return; } */ NSURLCredential* credential = [NSURLCredential credentialForTrust: trust]; [[challenge sender] useCredential: credential forAuthenticationChallenge: challenge]; } else { [[challenge sender] cancelAuthenticationChallenge: challenge]; } } else { [[challenge sender] cancelAuthenticationChallenge: challenge]; } }}