Dans iOS, comment se connecter à un server en utilisant https avec un certificate auto-signé sur le server?

Je suis en train de développer pour iOS 5 et je ne veux vraiment pas utiliser de codes non-ARCed donc j'ai choisi de l'implémenter moi-même au lieu d'utiliser AFNetworking. Aussi, cela pourrait être une grande question, donc je l'ai divisé en deux parties plus petites.

1) login au server en utilisant https dans iOS 5. J'utilise ici les codes extraits de "iOS 5 Programming Pushing the Limits". Parce que je suis en train de développer pour iOS 5, je n'utilise pas les methods obsolètes dans mon projet. "RNSecTrustEvaluateAsX509" est une méthode qui réévalue le certificate en tant que simple certificate X.509 plutôt que dans le cadre d'une prise de contact 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); CFSsortingngRef subject = SecCertificateCopySubjectSummary(cert); NSLog(@"Trying to access %@. Got %@.", protSpace.host, (__bridge id)subject); CFRange range = CFSsortingngFind(subject, CFSTR("192.168.1.100"), kCFCompareAnchored|kCFCompareBackwards); if (range.location != kCFNotFound) { NSLog(@"Creating new trust certificatee.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]; } } 

Il se connecte au server mais je reçois toujours une erreur disant "L'opération n'a pas pu être complétée (erreur NSURLErrorDomain -1012)". Et la console affiche "Failure dû au résultat 5", ce qui signifie que j'obtiens un kSecTrustResultRecoverableTrustFailure. Je suppose que c'est parce que j'utilise un certificate auto-signé sur le server. Cela conduit au deuxième problème ci-dessous.

2) Le certificate auto-signé provoque des problèmes. J'ai donc ajouté ces lignes

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

juste avant

 OSStatus status = SecTrustEvaluate(trust, &result); 

dans la méthode willSendRequestForAuthenticationChallenge ci-dessus. et j'ai aussi créé une méthode:

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

J'ai défini CA_CERTS en tant que données de certificate au format "der", qui est un NSSsortingng que j'ai obtenu du server via SecCertificateCopyData. Mais je continue d'get kSecTrustResultRecoverableTrustFailure. Je ne sais pas vraiment si je fais la bonne chose ici. Comment puis-je valider manuellement le certificate auto-signé du server en utilisant ses propres données? Plus précisément, comment get ses données à partir d'iOS?

Je suggère d'intégrer OpenSSL dans votre projet pour gérer les certificates et les défis d'autorisation! puis dans votre méthode 'connection: didReceiveAuthenticationChallenge:' du protocole 'NSURLConnectionDelegate', faites quelque chose comme ceci:

 - (void) connection:(NSURLConnection*) connection didReceiveAuthenticationChallenge: (NSURLAuthenticationChallenge*) challenge { if ([[[challenge protectionSpace] authenticationMethod] isEqualToSsortingng: NSURLAuthenticationMethodServerTrust]) { SecTrustRef trust = [[challenge protectionSpace] serverTrust]; NSMutableArray* certificatees = [NSMutableArray array]; NSData* certificatee2Data = // your certificatee data NSData* certificatee3Data = // even more certificatee data if needed SecCertificateRef certificatee2 = SecCertificateCreateWithData(NULL, (CFDataRef) certificatee2Data); SecCertificateRef certificatee3 = SecCertificateCreateWithData(NULL, (CFDataRef) certificatee3Data); [certificatees addObject: (id) certificatee2]; [certificatees addObject: (id) certificatee3]; CFRelease(certificatee2); CFRelease(certificatee3); SecTrustSetAnchorCertificates(trust, (CFArrayRef) certificatees); 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* certificateeDataBytes = (const unsigned char *)[leafCertificateData bytes]; X509* certificateeX509 = d2i_X509(NULL, &certificateeDataBytes, [leafCertificateData length]); CFRelease(leafCertificateData); X509_NAME *issuerX509Name = X509_get_issuer_name(certificateeX509); X509_NAME *subjectX509Name = X509_get_subject_name(certificateeX509); /* with issuerX509Name and subjectX509Name you could check some properties of the certificatee and cancel the authentication challenge fe! if ([[self valueWithKey: @"CN" inName: subjectX509Name cert: certificateeX509] isEqualToSsortingng: @"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]; } }}