Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I accept self-signed server certificates from a whitelist in iOS?

I'm trying to accept self-signed certificates in a NSURLConnection, as many have before me. The catch is that I only want to accept certs from a whitelist of certs I trust. I'd settle for figuring out how to accept a single cert. Here's the code I've got so far in my NSURLConnectionDelegate:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        NSString *thePath = [[NSBundle mainBundle] pathForResource:@"trusted" ofType:@"der"];
        NSData *certData = [[NSData alloc] initWithContentsOfFile:thePath];
        CFDataRef myCertData = (__bridge_retained CFDataRef)certData;
        SecCertificateRef myCert = SecCertificateCreateWithData(NULL, myCertData);
        SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
        SecCertificateRef certArray[1] = { myCert };
        CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
        SecTrustRef myTrust;
        OSStatus status = SecTrustCreateWithCertificates(myCerts, myPolicy, &myTrust);

        SecTrustResultType trustResult;
        if (status == noErr) {
            status = SecTrustEvaluate(myTrust, &trustResult);
        }
        BOOL trusted = NO;

        if (trustResult == kSecTrustResultUnspecified) {
            // I never get here.  Instead, trustResult is always kSecTrustResultRecoverableTrustFailure
            trusted = YES; 
        }

        if (trusted) {
            [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]
                 forAuthenticationChallenge:challenge];
        } else {
            [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
        }
        CFRelease(myTrust);
        CFRelease(myCerts);
        CFRelease(myPolicy);
        CFRelease(myCert);
        CFRelease(myCertData);
    } else {
        [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
    }
}

As you can see in the comment, I never actually get kSecTrustResultUnspecified, which is what I expect to get. I verified that my cert is correct, and in the correct format (DER).

like image 967
Micah Hainline Avatar asked May 23 '12 20:05

Micah Hainline


People also ask

How do I add a self-signed certificate to trusted Iphone?

If you want to turn on SSL/TLS trust for that certificate, go to Settings > General > About > Certificate Trust Settings. Under "Enable full trust for root certificates," turn on trust for the certificate. Apple recommends deploying certificates via Apple Configurator or Mobile Device Management (MDM).

How do I accept a self-signed certificate?

Navigate to the site with the cert you want to trust, and click through the usual warnings for untrusted certificates. In the address bar, right click on the red warning triangle and "Not secure" message and, from the resulting menu, select "Certificate" to show the certificate.

How do I trust certificates on Iphone IOS 15?

The user can then trust the certificate on the device by going to Settings > General > About > Certificate Trust Settings.


1 Answers

Okay, figured it out. It turns out you just need to check the server trust, and actually use the cert data.

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    BOOL trusted = NO;
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        NSString *thePath = [[NSBundle mainBundle] pathForResource:@"trusted" ofType:@"der"];
        NSData *certData = [[NSData alloc] initWithContentsOfFile:thePath];
        CFDataRef certDataRef = (__bridge_retained CFDataRef)certData;
        SecCertificateRef cert = SecCertificateCreateWithData(NULL, certDataRef);
        SecPolicyRef policyRef = SecPolicyCreateBasicX509();
        SecCertificateRef certArray[1] = { cert };
        CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
        SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
        SecTrustSetAnchorCertificates(serverTrust, certArrayRef);
        SecTrustResultType trustResult;
        SecTrustEvaluate(serverTrust, &trustResult);
        trusted = (trustResult == kSecTrustResultUnspecified);
        CFRelease(certArrayRef);
        CFRelease(policyRef);
        CFRelease(cert);
        CFRelease(certDataRef);
    }
    if (trusted) {
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
    } else {
        [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
    }
}
like image 98
Micah Hainline Avatar answered Oct 21 '22 02:10

Micah Hainline