2

I am using sha1WithRSAEncryption (i.e. not MD5 as per SecTrustEvaluate returns kSecTrustResultRecoverableTrustFailure on iOS 5) server certs generated with a simple ca->subca->server lineage.

openssl req -new -x509 -subj /CN=ca/O=1002 -nodes -keyout /dev/stdout -set_serial 4 -days 3650  > ca-1002.pem
openssl req -new       -subj /CN=sub-ca/O=1002 -nodes -keyout subca-1002.pem -set_serial 5 -days 3650 | openssl x509 -CA ca-1002.pem -req -set_serial 1 >> subca-1002.pem
openssl req -new       -subj /CN=localhost:2002/O=1002 -nodes -keyout cert-1002.pem -set_serial 6 -days 3650 | openssl x509 -CA subca-1002.pem -req -set_serial 1 >> cert-1002.pem

for the server; with a chain for the servers SSLCACertificateFile and a DER file to be read in by the iPhone/iPad app later.

cat ca-1002.pem subca-1002.pem > chain-1002.pem
openssl x509 -in ca-1002.pem -outform DER -out ca-1002.der

I make sure I get a say in the evaluation with:

-(BOOL)connection:(NSURLConnection *)aConnection 
   canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)space
 {
     DLog(@" authenticationMethod: %@",[space authenticationMethod]);

     // nil/not set - rely on the keychain, set to an empty array indicates
     // we know what we're doing -and accept anything - otherwise a list
     // of certs gotten from reading in DER files.
     //
     NSArray *acceptableCAs = [delegate sslAcceptableServerCACertificates];

     if ([[space authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]) {
         NSArray *acceptableCAs = [delegate sslAcceptableServerCACertificates];
         return acceptableCAs != nil ? YES : NO;
     }
     // Client cert stuff snipped.

     // the keychain is used.
     return NO;
}

So far so good.

-(void)connection:(NSURLConnection *)connection 
    didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    NSURLProtectionSpace * space = [challenge protectionSpace];


if ([space.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
    NSArray *acceptableCAs = [delegate sslAcceptableServerCACertificates];
    NSURLCredential *newCredential;

    if (acceptableCAs == nil) { ... }
        // We'll leave this to the keychain by doing nothing. (works splendidly)
    else if ([acceptableCAs count] == 0) { }
        // We'll accept 'anything' - or in this case - exactly this server.
        // (this works just fine).

works - but when I specific 1 or more certs explicity - things fail:

    else {
        SecTrustRef secTrustRef =  challenge.protectionSpace.serverTrust;
        SecTrustResultType result;
        NSMutableArray * serverChain = [[NSMutableArray alloc] init];
        for(long i = 0; i < SecTrustGetCertificateCount(secTrustRef); i++) {
            SecCertificateRef cr = SecTrustGetCertificateAtIndex(secTrustRef, i);
            DLog(@"Server cert %04ld: %@", i, cr);
            DLog(@"     %@", [SimpleCertificate sha1fingerprint:cr]);
            [serverChain addObject:(__bridge id)(cr)];
        }

        for(NSUInteger i = 0; i < [acceptableCAs count]; i++) { .. dump these too }

        SecTrustSetAnchorCertificatesOnly(secTrustRef, YES);
        SecTrustSetAnchorCertificates(secTrustRef, (__bridge CFArrayRef)acceptableCAs);
//            SecTrustSetAnchorCertificatesOnly(secTrustRef, NO);
//            assert(SecTrustCreateWithCertificates((__bridge CFTypeRef)(serverChain), SecPolicyCreateSSL(YES, nil), &secTrustRef) == noErr);
//            assert(SecTrustCreateWithCertificates((__bridge CFTypeRef)(serverChain), SecPolicyCreateSSL(NO, nil), &secTrustRef) == noErr);
//            assert(SecTrustCreateWithCertificates((__bridge CFTypeRef)(serverChain), SecPolicyCreateBasicX509(), &secTrustRef) == noErr);
//            assert(SecTrustSetVerifyDate(secTrustRef, ...);

        assert(SecTrustEvaluate(secTrustRef, &result) == noErr);            
        DLog(@"SecTrustEvaluate gives us: %lu", result);

       ....

I get the error below. SecTrustEvaluate always gives me back a 5 (and sometimes a 4).

Have tried various suggestions; such as date shifting, verified it is sha1 and not md5, etc.

athAgstPrSp:] [Line 68]  authenticationMethod: NSURLAuthenticationMethodServerTrust
didRecAChlg:] [Line 98]  authenticationMethod: NSURLAuthenticationMethodServerTrust
didRecAChlg:] [Line 143] Server cert 0000: <cert(0xd637ce0) s: localhost:2002 i: sub-ca>
didRecAChlg:] [Line 144]      8D:21:95:20:11:D0:9C:27:86:56:B3:0B:A6:F7:A2:CF:4C:F6:67:64
didRecAChlg:] [Line 143] Server cert 0001: <cert(0xd638710) s: sub-ca i: ca>
didRecAChlg:] [Line 144]      30:58:8E:F9:B8:29:5E:84:46:E3:0B:98:0D:B0:28:23:3D:7E:86:A3
didRecAChlg:] [Line 143] Server cert 0002: <cert(0xd638af0) s: ca i: ca>
didRecAChlg:] [Line 144]      8E:A2:D9:13:AF:8B:AE:82:1C:39:E9:79:72:98:2A:B4:C0:17:0D:F0
didRecAChlg:] [Line 150] CA cert 0000: <cert(0xf0397f0) s: ca i: ca>
didRecAChlg:] [Line 151]      8E:A2:D9:13:AF:8B:AE:82:1C:39:E9:79:72:98:2A:B4:C0:17:0D:F0
didRecAChlg:] [Line 159] SecTrustEvaluate gives us: 5

Note - I am trying to avoid the manual compare with the SHA1 of the main answer of: Does SecTrustEvaluate() look for root certificates in the application keychain? as to rely as cleanly as possible on the keychain and what not. As I have several other use cases where a chipcard and a keychain is involved. Hostnames in above example match. In above - the server 001..003 certs match 1:1 what is seen by openssl s_client; and the CA cert is indeed the DER file provided (and identical to server flashed CA cert 002). We tried also setting SubjectAltNames on CA's and clients; and did not see that make a difference (as suggested by SecTrustEvaluate always returns kSecTrustResultRecoverableTrustFailure with SecPolicyCreateSSL) which makes sense as wee know that manually importing them in keychain/profile makes the very same certs work perfectly fine as default keychain objects.

Any and all suggestions would be welcome. Looking for a clean way to say in the NSURLConnectionDelegate to either accept the keychain, to accept 'anything' or to just accept something specific.

Thanks,

Dw.

Community
  • 1
  • 1
Dirk-Willem van Gulik
  • 7,566
  • 2
  • 35
  • 40

0 Answers0