We are pushing a root cert via MDM so that the cert and its child certs are implicitly trusted by the device (all iOS devices). But when we try validating cert for a leaf node cert its failing with
kSecTrustResultRecoverableTrustFailure
Error Domain=SecurityTrustEvaluate Code=5 "Root certificate is not trusted." UserInfo={NSLocalizedDescription=Root certificate is not trusted.}
Steps:
- Enroll into MDM
- Push the Root and Intermediate cert to the client as a credential payload
- Our App installs the leaf certificate in its keychain
- It then validates the leaf cert using the following code
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef trust;
SecTrustCreateWithCertificates(leafcert, policy, &trust);
SecTrustResultType trustResult;
SecTrustEvaluate(trust, &trustResult);
BOOL trusted = (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed);
Based on apple docs https://developer.apple.com/documentation/security/1399071-sectrustsetanchorcertificatesonl?language=objc
Alternatively tried:
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef trust;
SecTrustCreateWithCertificates(leafcert, policy, &trust);
SecTrustSetAnchorCertificates(trust, (CFArrayRef) [NSArray array]);
SecTrustSetAnchorCertificatesOnly(trust, NO);
SecTrustResultType trustResult;
BOOL trusted = (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed);
In both cases it returns !trusted and the error is:
kSecTrustResultRecoverableTrustFailureError Domain=SecurityTrustEvaluate Code=5 "Root certificate is not trusted." UserInfo={NSLocalizedDescription=Root certificate is not trusted.}
Ah, I missed the fact that you have an intermediate certificate in play. The problem here is that iOS has two places to store certificates:
A root certificate is installed in the trust store
Intermediate and leaf certificates are installed in the keychain
The issue here is that the keychain has an access control mechanism known as keychain access groups. When you install a non-root certificate via a configuration profile, it goes into an Apple-only keychain access group. Your app is unable to access such items, and thus the trust evaluation fails.
The best way around this is for whoever gave you the leaf certificate to also give you the intermediates leading from the leaf to the root. This is common practice, support by all the standard technologies for distributing certificates. For example, TLS requires that the server present to the client the entire certificate chain (except the root) to the client [1].
So, where do you get the leaf from? And can you get the intermediate at the same time?
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"[1] Specifically, Section 7.4.2 of RFC 5246 says:
The sender's certificate MUST come first in the list. Each following certificate MUST directly certify the one preceding it. Because certificate validation requires that root keys be distributed independently, the self-signed certificate that specifies the root certificate authority MAY be omitted from the chain, under the assumption that the remote end must already possess it in order to validate it in any case.