How to evaluate SSL wildcard certificate?

I have an enterprise app that requires SLL Mutual Authentication. The server has a wildcard certifacte *.xxx.yyy. When I execute SecTrustEvaluate, I get success, with a result of kSecTrustResultRevocerableTrustFailure. I believe due to the wildcard CN.


Currently I am handling the recoverable failure on my (SecTrustRef) trust object by:


CFDataRef errDataRef = SecTrustCopyExceptions(trust);

SecTrustSetExceptions(trust, errDataRef);

returnCode - SecTrustEvaluate(trust, &result);

proceed = result == kSecTrustResultProceed;


Of course that "works", but it appears that any domain is now trusted - certainly not what I want.


How do

  • examine the failure, making sure the failure is due to the wildcard domain expression,
  • ensure the server matches the domain,
  • set the SecTrustSetExceptions() to only ignore the domain "recoverable failure" rather than every recoverable failure that may have occured - e.g. expired cert


Thanks

I believe due to the wildcard CN.

That’s unlikely. If you (or the system) has created the trust object with the TLS policy (

SecPolicyCreateSSL
), it will handle wildcards per RFC 2818.

I have an enterprise app that requires [TLS] Mutual Authentication.

In what context are you doing the trust evaluation? In most cases you don’t need customise trust evaluation in either case:

  • For client-authenticates-server trust evaluation (in a NSURL{Session,Connection} context, this is

    NSURLAuthenticationMethodServerTrust
    ), the system’s default HTTPS server trust evaluation is usually sufficient. In the enterprise context you may need to ensure that your device has the enterprise’s CA installed as a trusted root. This being missing is the most common cause for
    kSecTrustResultRevocerableTrustFailure
    .

    A good first step in debugging this sort of trust evaluation problem is to call

    SecTrustCopyResult
    and
    SecTrustCopyProperties
    to see what the trust object is complaining about.
  • For server-authenticates-client trust evaluation (

    NSURLAuthenticationMethodClientCertificate
    ), you don’t need to do any trust evaluation on the client. You should just pass the client digital identity to the TLS infrastructure, which passes the client certificate to the server, which does its own client trust evaluation however it sees fit.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks Quinn- that helped!


In hindsight it looks like I omitted a couple of important details. First, yes the context is NSURLAuthenticationMethodServerTrust


The customer (large retail enterprise), has an in-house CA - i.e., the self-signed root CA is not included in iOS by default. They use AirWatch to push the app to thousands of devices, but AirWatch cannot push the root certificate for the in-house CA. They want the app to "install it" - and they don't want the user to have to accept the certificate.


We are using on old of RestKit, which has a facility to handle additional root certificates (see code snip below). Your suggestions were helpful and I now see that SecTrustCopyResult() returns:

AnchorTrusted = 0;

BasicContraints = 0;

and SecTrustCopyProperties() returns:

{

type = error;

value = "Root certificate is not trusted.";

},

{

type = error;

value = "Policy requirements not met.";

}


The customer supplied the root certificate in .der format - is something wrong with the .der, or am I missing some other piece of the puzzle?


- (BOOL)isServerTrusted:(SecTrustRef)trust {
    RKClient* client = [RKClient sharedClient];
    BOOL proceed = NO;
    if (client.disableCertificateValidation) {
        proceed = YES;
    } else {
        CFArrayRef rootCerts = (CFArrayRef)[client.additionalRootCertificates allObjects];
        SecTrustResultType result;
        OSStatus returnCode;
  
        if (rootCerts && CFArrayGetCount(rootCerts)) {
            SecTrustSetAnchorCertificates(trust, rootCerts);
        }
        SecTrustSetAnchorCertificatesOnly(trust, true);
  
        returnCode = SecTrustEvaluate(trust, &result);
  
        if (returnCode == errSecSuccess) {
            proceed = (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified);
            if (result == kSecTrustResultRecoverableTrustFailure) {
                CFDictionaryRef resultDictionary = SecTrustCopyResult(trust);
                NSLog(@"resultDictionary = %@", resultDictionary);
                CFRelease(resultDictionary);
                CFArrayRef propertyArray = SecTrustCopyProperties(trust);
                NSLog(@"propertyArray = %@", propertyArray);
                CFRelease(propertyArray);
                /*
                Recovery examples include accepting "wildcard" domain names and accepting
                certificates that have expired but were valid at the time they were issued.
            
                In OS X, you can call SecTrustGetCssmResult() for more information about the failure,
                but there doesn't seem any way to get more details in iOS.
                */
                // the following code is too permisive - basically is allows everything
                CFDataRef errDataRef = SecTrustCopyExceptions(trust);
                NSLog(@"errDataRef=%@", errDataRef);
                SecTrustSetExceptions(trust, errDataRef);
          
                returnCode = SecTrustEvaluate(trust, &result);
                proceed = result == kSecTrustResultProceed;
            }
        }
    }
    return proceed;
}

The customer (large retail enterprise), has an in-house CA - i.e., the self-signed root CA is not included in iOS by default. They use AirWatch to push the app to thousands of devices, but AirWatch cannot push the root certificate for the in-house CA.

Huh? it’s absolutely standard for enterprises to have their own root CA and for MDM solutions to push that root CA to devices. I strongly recommend you push because on this because, while you can work around it on the client, that workaround has a number of drawbacks:

  • It requires you to write code, which is always best to avoid if you can.

  • Any mistake in that code is likely to result in a serious security vulnerability.

  • That code requires you to disable App Transport Security for the domains in question, which is poor form in general.

If, just for the sake of testing, you install the root certificate on the device, do things work?

With regards the code you posted, let me start by saying that the very existence of your

disableCertificateValidation
option is a mistake IMO. That’s a classic gateway for security bugs. You shouldn’t ever need to this because:
  • while testing you should set up a test CA (there’s advice for doing this in Technote 2326 Creating Certificates for TLS Testing)

  • in production you always want to do property trust evaluation

As to why things are failing, it’s hard to say based on the info presented. The “Root certificate is not trusted” implies that you can’t find a path from the leaf certificate of the server to a trust root. Given that you’ve supplied the expected trusted root, that means that either the server isn’t using the leaf issued by that root or the server isn’t pass you all the intermediates required to go from its leaf to that root. What is the certificate chain supposed to look like? What do you get back when you dump the certificate chain from the trust object? You can do that using

SecTrustGetCertificateCount
and
SecTrustGetCertificateAtIndex
.

With regards “Policy requirements not met”, that’s definitely a weird one. The last time I saw this was during the earlier iOS 9 release cycle, where iOS was strictly enforcing the Name Constraints extension. What I recommend you do here is first resolve the certificate chain problem and then come back to this one.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
How to evaluate SSL wildcard certificate?
 
 
Q