No iOS, como se conectar a um servidor usando https com certificado autoassinado no servidor?
Estou desenvolvendo para o iOS 5 e realmente não quero usar códigos un-ARCed, então escolhi implementar isso sozinho, em vez de usar o AFNetworking. Também isso pode ser uma grande questão, então eu dividi em duas partes menores.
1) Conectando ao servidor usando https no iOS 5. Eu uso os códigos extraídos de "iOS 5 Programming Pushing the Limits" aqui. Como estou desenvolvendo para o iOS 5, não uso os métodos obsoletos no meu projeto. "RNSecTrustEvaluateAsX509" é um método que reavalia o certificado como um simples certificado X.509 em vez de como parte de um handshake 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];
}
}
Ele se conecta ao servidor, mas eu sempre recebo um erro dizendo "A operação não pôde ser concluída (erro NSURLErrorDomain -1012)". E o console mostra "Falha devido ao resultado 5", o que significa que recebo um kSecTrustResultRecoverableTrustFailure. Eu suspeito que isso é porque eu estou usando o certificado auto-assinado no servidor. Isso leva ao segundo problema como abaixo.
2) O certificado autoassinado está causando problemas. Então eu adicionei essas linhas
// Self-signed certificates need to be validated manually.
NSArray *anchors = [self serverAnchors];
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)anchors);
SecTrustSetAnchorCertificatesOnly(trust, YES);
pouco antes
OSStatus status = SecTrustEvaluate(trust, &result);
no método willSendRequestForAuthenticationChallenge acima. e também criei um método:
- (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;
}
Eu defini CA_CERTS como os dados do certificado de formato "der", que é uma NSString que obtive do servidor via SecCertificateCopyData. Mas ainda continuo recebendo o kSecTrustResultRecoverableTrustFailure. Eu realmente não sei se estou fazendo a coisa certa aqui. Como posso validar manualmente o certificado autoassinado do servidor usando seus próprios dados? Mais especificamente, como obter seus dados do iOS?