W iOS, jak połączyć się z serwerem za pomocą https z certyfikatem z podpisem własnym na serwerze?
Zajmuję się tworzeniem iOS 5 i naprawdę nie chcę używać kodów nie-ARCed, więc zdecydowałem się go wdrożyć, zamiast używać AFNetworking. To może być również duże pytanie, więc podzieliłem je na dwie mniejsze części.
1) Łączenie się z serwerem za pomocą https w systemie iOS 5. Używam tutaj kodów wyodrębnionych z „Programowania iOS 5, przesuwanie granic”. Ponieważ pracuję nad iOS 5, nie używam przestarzałych metod w moim projekcie. „RNSecTrustEvaluateAsX509” to metoda, która ponownie ocenia certyfikat jako prosty certyfikat X.509, a nie jako część uzgadniania 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];
}
}
Łączy się z serwerem, ale zawsze pojawia się błąd „Nie można ukończyć operacji (błąd NSURLErrorDomain -1012)”. Konsola pokazuje „Niepowodzenie z powodu wyniku 5”, co oznacza, że otrzymuję kSecTrustResultRecoverableTrustFailure. Podejrzewam, że to dlatego, że używam certyfikatu z podpisem własnym na serwerze. Prowadzi to do drugiego problemu, jak poniżej.
2) Certyfikat z podpisem własnym powoduje problemy. Dodałem więc te linie
// Self-signed certificates need to be validated manually.
NSArray *anchors = [self serverAnchors];
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)anchors);
SecTrustSetAnchorCertificatesOnly(trust, YES);
przed chwilą
OSStatus status = SecTrustEvaluate(trust, &result);
w powyższej metodzie willSendRequestForAuthenticationChallenge. a także stworzyłem metodę:
- (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;
}
Zdefiniowałem CA_CERTS jako dane certyfikatu formatu „der”, który jest NSString otrzymany z serwera przez SecCertificateCopyData. Ale nadal otrzymuję kSecTrustResultRecoverableTrustFailure. Naprawdę nie wiem, czy robię to dobrze. Jak ręcznie zweryfikować certyfikat samopodpisany z serwera przy użyciu własnych danych? Dokładniej, jak uzyskać dane z iOS?