Question

I have two certificates in X509 format, and I want to add certificateA to the list of anchor certificates, and evaluate certificateB against ONLY certificateA. Let's also say certA -> certB in the chain, so CertA is the trusted root.

int len = i2d_X509(certificateA, &buf);
if (len > 0) {
  /* Translate the data to a SecCertificateRef */
  CFDataRef data = CFDataCreate(NULL, buf, len);
  SecCertificateRef ref = SecCertificateCreateWithData(NULL, data);
  CFRelease(data);

  if (ref != NULL) {
    /* Add the cert to the array */
    [certs addObject:(__bridge_transfer id)(ref)];
  }
} else {
  return NULL;
}
OPENSSL_free(buf);

/* Get the reference */
CFArrayRef certsRef = (__bridge CFArrayRef)certs;

/* Get the Trust Refs */
NSString *refHostname = [NSString stringWithCString:hostname.c_str() encoding:[NSString defaultCStringEncoding]];
SecPolicyRef policy = SecPolicyCreateSSL(NO, (__bridge CFStringRef) refHostname);

SecTrustRef trustRefA;
OSStatus ret = SecTrustCreateWithCertificates(certsRef, policy, &trustRefA);

I do this for both certA and certB, which gives me two trustRefs. Then to add certificateA to the Anchors list I do:

  OSStatus ret = SecTrustSetAnchorCertificatesOnly(trustRefA, YES);

Later on, I want to do:

  OSStatus ret = SecTrustEvaluate(trustRefB, YES);

This doesn't work. Also, is there a way that I can use SecTrustEvaluate, and only evaluate against the CertA anchor.

Is there any way to set a single anchor as the default certificate to verify against? I'm fairly confused because I thought that's what SecTrustSetAnchorCertificateOnly() did.

Was it helpful?

Solution

Here's the code to add to NSURLConnectionDelegate's connection:didReceiveAuthenticationChallenge:. It loads a CA in DER format, and verifies a particular server's against that one CA.

if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust])
{
    do
    {
        SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
        if(nil == serverTrust)
            break; /* failed */

        NSData* caCert = [NSData dataWithContentsOfFile:@"ca-rsa-cert.der"];
        if(nil == caCert)
            break; /* failed */

        SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
        if(nil == caRef)
            break; /* failed */

        NSArray* caArray = [NSArray arrayWithObject:(__bridge id)(caRef)];
        if(nil == caArray)
            break; /* failed */

        OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
        if(!(errSecSuccess == status))
            break; /* failed */

        SecTrustResultType result = -1;
        status = SecTrustEvaluate(serverTrust, &result);
        if(!(errSecSuccess == status))
            break; /* failed */

        /* https://developer.apple.com/library/ios/technotes/tn2232/_index.html */
        /* https://developer.apple.com/library/mac/qa/qa1360/_index.html */
        /* kSecTrustResultUnspecified and kSecTrustResultProceed are success */
        if(result != kSecTrustResultUnspecified && result != kSecTrustResultProceed)
            break; /* failed */           

        // The only good exit point
        return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust]
                      forAuthenticationChallenge: challenge];

    } while(0);
}

// Bad dog
return [[challenge sender] cancelAuthenticationChallenge: challenge];

If you call [connection cancel]; on the bad path, then connection:didFailWithError: will not be invoked.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top