6

I'd like to validate my ssl server certificates with some extra checks. And sometimes I get a

kSecTrustResultRecoverableTrustFailure 

instead of

kSecTrustResultProceed or kSecTrustResultUnspecified

It seems to happen if

  • the certificate is md5 hashed (IOS5)
  • the server does not present the root and intermediate certificates
  • the SecTrustSetAnchorCertificatesOnly(trust,YES) is set and the anchor certificate is only in the built in anchor certificates
  • the certificate is expired
  • ?

It depends on the AppleX509TP policy used to evaluate the trust.

My problem is I do not want to trust if the chain fails, but I want to trust if MD5 is used.

Is there a way to find out why the evaluation failed?

As an alternative is there a way to extract the CSSM_ALGID_MD5 from a SecCertificateRef?

n3utrino
  • 2,361
  • 3
  • 22
  • 32

2 Answers2

3

Is there a way to find out why the evaluation failed?

Call SecTrustCopyProperties() after calling SecTrustEvaluate():

SecTrustRef trust = ...;
SecTrustResultType trustResult = kSecTrustResultOtherError;
OSStatus status = SecTrustEvaluate(trust, &trustResult);
if (trustResult == kSecTrustResultRecoverableTrustFailure) {
    NSArray * trustProperties = (__bridge_transfer id)
        SecTrustCopyProperties(certTrust);
}

trustProperties is an array of dictionaries, one dictionary per cert in the cert chain evaluated. Every dictionary has an entry title, containing the name of the cert and if the cert didn't evaluate, it also contains an entry error containing the error. E.g. if the problem was that the cert has expired, the value of error will be CSSMERR_TP_CERT_EXPIRED.

Mecki
  • 125,244
  • 33
  • 244
  • 253
3

It may be a server certificate problem....

Check here, I solved my kSecTrustResultRecoverableTrustFailure problem, adding subjectAltName = DNS:example.com into openssl config file, specifically in server key generation...

If you are not using openssl to generate it, I'm sorry but I can help you.. Anyway if you want to use openssl, here is a good tutorial to generate those keys and sign then with your own root certificate authority.

From this tutorial, I just changed my openssl server config file to:

    [ server ]
    basicConstraints = critical,CA:FALSE
    keyUsage = digitalSignature, keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth
    nsCertType = server
    subjectAltName = IP:10.0.1.5,DNS:office.totendev.com
    

Hope it helps !

EDITED:

My Server evaluation code:

#pragma mark - SERVER Auth Helper
//Validate server certificate with challenge
+ (BOOL)validateServerWithChallenge:(NSURLAuthenticationChallenge *)challenge {
//Get server trust management object a set anchor objects to validate it
SecTrustSetAnchorCertificates([challenge.protectionSpace serverTrust], (__bridge CFArrayRef)[self allowedCAcertificates]);
//Set to server trust management object to JUST ALLOW those anchor objects assigned to it (ABOVE), and disable apple CA trusts 
SecTrustSetAnchorCertificatesOnly([challenge.protectionSpace serverTrust], YES);
//Try to evalute it
SecTrustResultType evaluateResult = kSecTrustResultInvalid; //evaluate result
OSStatus sanityCheck = SecTrustEvaluate([challenge.protectionSpace serverTrust], &evaluateResult);
//Check for no evaluate error
if (sanityCheck == noErr) {
    //Check for result
    if ([[self class] validateTrustResult:evaluateResult]) { return YES ; }
}
//deny!
return NO ;
}
//Validate SecTrustResulType
+ (BOOL)validateTrustResult:(SecTrustResultType)result {
switch (result) {
    case kSecTrustResultProceed: { TDLog(kLogLevelHandshake,nil,@"kSecTrustResultProceed"); return YES ; }
        break;
    case kSecTrustResultConfirm: { TDLog(kLogLevelHandshake,nil,@"kSecTrustResultConfirm"); return YES ; }
        break;
    case kSecTrustResultUnspecified: { TDLog(kLogLevelHandshake,nil,@"kSecTrustResultUnspecified"); return YES ; }
        break;
    case kSecTrustResultDeny: { TDLog(kLogLevelHandshake,nil,@"kSecTrustResultDeny"); return YES ; }
        break;
    case kSecTrustResultFatalTrustFailure: { TDLog(kLogLevelHandshake,nil,@"kSecTrustResultFatalTrustFailure"); return NO ; }
        break;
    case kSecTrustResultInvalid: { TDLog(kLogLevelHandshake,nil,@"kSecTrustResultInvalid"); return NO ; }
        break;
    case kSecTrustResultOtherError: { TDLog(kLogLevelHandshake,nil,@"kSecTrustResultOtherError"); return NO ; }
        break;
    case kSecTrustResultRecoverableTrustFailure: { TDLog(kLogLevelHandshake,nil,@"kSecTrustResultRecoverableTrustFailure"); return NO ; }
        break;
    default: { TDLog(kLogLevelHandshake,nil,@"unkown certificate evaluate result type! denying..."); return NO ; }
        break;
}

}

Hope now it helps :) !

Community
  • 1
  • 1
gwdp
  • 1,202
  • 10
  • 21
  • thanks for your suggestion. But my problem is I don't issue the certificates myself. – n3utrino Feb 10 '12 at 16:27
  • So your problem is your CA certificate or in your evaluation! How are you importing your key ? From each source , is a File ? Edited in my comment is my evaluation code... – gwdp Mar 02 '12 at 07:20
  • Hi. Thanks for updating. My problem is how do I get the cause of a kSecTrustResultRecoverableTrustFailure because somtimes I want to return YES. – n3utrino Mar 02 '12 at 14:45
  • Hm.... you probably will need to go deeper ! Have you tried to make your own expiration validation as http://stackoverflow.com/questions/5340937/continuously-getting-ksectrustresultrecoverabletrustfailure-while-trust-evaluati ?? – gwdp Mar 03 '12 at 07:06
  • Probably If I want to know the cause of my 'kSecTrustResultRecoverableTrustFailure' I'll make each validation for my own, so I can know where it's failure. Hard work just to know what is causing and may accept it.It would be better to just accept good certificates... – gwdp Mar 03 '12 at 07:16