SecTrustEvaluate() failing with kSecTrustResultRecoverableTrustFailure

  • I'm trying to validate our self-signed certificate in NSURLConnectionDelegate's -connection:willSendRequestForAuthenticationChallenge: using itself as the root cert. I'm not 100% sure I'm doing it right, but looking at the ridiculously, excessively complicated example code, I've come up with this:

    mRootCert holds a certificate I created with:

    mRootCert = SecCertificateCreateWithData(kCFAllocatorDefault, CFBridgingRetain(inRootCert));

    - (void)
    connection: (NSURLConnection*) inConnection
        willSendRequestForAuthenticationChallenge: (NSURLAuthenticationChallenge*) inChallenge
    {
        NSLog(@"Connection challenged");
        NSURLProtectionSpace* protectionSpace = inChallenge.protectionSpace;
        SecTrustRef trust = protectionSpace.serverTrust;

        NSArray* certs = @[ CFBridgingRelease(mRootCert) ];
        OSStatus err = SecTrustSetAnchorCertificates(trust, CFBridgingRetain(certs));
        if (err == noErr)
        {
            SecTrustResultType trustResult;
            err = SecTrustEvaluate(trust, &trustResult);
            bool trusted = err == noErr;
            trusted = trusted && (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified);
            if (trusted)
            {
                NSURLCredential* credential = [NSURLCredential credentialForTrust: trust];
                [inChallenge.sender useCredential: credential forAuthenticationChallenge: inChallenge];
                return;
            }
        }

        //  An error occurred, or we don't trust the cert, so disallow it…

        [inChallenge.sender cancelAuthenticationChallenge: inChallenge];
    }

    Unfortunately, the result of evaluating the trust is: kSecTrustResultRecoverableTrustFailure, which the headers describe as:

    > kSecTrustResultRecoverableTrustFailure Indicates a trust
    > framework failure; retry after fixing inputs. This value may be returned
    > by the SecTrustEvaluate function but not stored as part of the user
    > trust settings.

    Which really doesn't tell me anything.

    A couple of notes:

    - The cert is self-signed, and is its own root.
    - The hostname/address of the server to which I'm connecting is never fixed, and I'm not trying to validate against that. I only want to validate that the server's cert is the cert I expect.

    Anyone have any idea?

    Thanks!

    --
    Rick
  • On May 15, 2013, at 9:13 PM, Rick Mann <rmann...> wrote:

    > I'm trying to validate our self-signed certificate in NSURLConnectionDelegate's -connection:willSendRequestForAuthenticationChallenge: using itself as the root cert. I'm not 100% sure I'm doing it right, but looking at the ridiculously, excessively complicated example code, I've come up with this:

    Ugh, yeah, I’ve had trouble with this too, but it was long enough ago that I don’t remember the details.
    One thing that might help is calling SecTrustGetResult to get detailed info on the results of trust evaluation. The returned structure might have some clues in it.

    You can also ask for help on the apple-cdsa list (which is for security/crypto related topics).

    —Jens
  • On May 16, 2013, at 12:46 , Jens Alfke <jens...> wrote:

    > Ugh, yeah, I’ve had trouble with this too, but it was long enough ago that I don’t remember the details.
    > One thing that might help is calling SecTrustGetResult to get detailed info on the results of trust evaluation. The returned structure might have some clues in it.
    >
    > You can also ask for help on the apple-cdsa list (which is for security/crypto related topics).

    Thanks, Jens, I'll look into both of those.

    --
    Rick
  • I figured it out, finally. Because our server has a dynamically-assigned IP address, and no hostname, we couldn't create a cert that matched. By default, iOS and OS X want to verify the hostname. The solution is to create a new SecTrustRef with a policy that does not check the hostname, and evaluate that. The final code is:

    - (void)
    connection: (NSURLConnection*) inConnection
        willSendRequestForAuthenticationChallenge: (NSURLAuthenticationChallenge*) inChallenge
    {
        NSLogDebug(@"Connection challenged");

        //  Build a new trust based on the supplied trust, so that we can set the policy…

        NSURLProtectionSpace* protectionSpace = inChallenge.protectionSpace;
        SecTrustRef trust = protectionSpace.serverTrust;

        CFIndex numCerts = SecTrustGetCertificateCount(trust);
        NSMutableArray* certs = [NSMutableArray arrayWithCapacity: numCerts];
        for (CFIndex idx = 0; idx < numCerts; ++idx)
        {
            SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, idx);
            [certs addObject: CFBridgingRelease(cert)];
        }

        //  Create a policy that ignores the host name…

        SecPolicyRef policy = SecPolicyCreateSSL(true, NULL);
        OSStatus err = SecTrustCreateWithCertificates(CFBridgingRetain(certs), policy, &trust);
        CFRelease(policy);
        if (err != noErr)
        {
            NSLogDebug(@"Error creating trust: %d", err);
            [inChallenge.sender cancelAuthenticationChallenge: inChallenge];
            return;
        }

        //  Set the root cert and evaluate the trust…

        NSArray* rootCerts = @[ CFBridgingRelease(mRootCert) ];
        err = SecTrustSetAnchorCertificates(trust, CFBridgingRetain(rootCerts));
        if (err == noErr)
        {
            SecTrustResultType trustResult;
            err = SecTrustEvaluate(trust, &trustResult);
            CFRelease(trust);

            bool trusted = err == noErr;
            trusted = trusted && (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified);
            if (trusted)
            {
                NSURLCredential* credential = [NSURLCredential credentialForTrust: trust];
                [inChallenge.sender useCredential: credential forAuthenticationChallenge: inChallenge];
                return;
            }
        }

        //  An error occurred, or we don't trust the cert, so disallow it…

        [inChallenge.sender cancelAuthenticationChallenge: inChallenge];
    }

    On May 16, 2013, at 12:46 , Jens Alfke <jens...> wrote:

    >
    > On May 15, 2013, at 9:13 PM, Rick Mann <rmann...> wrote:
    >
    >> I'm trying to validate our self-signed certificate in NSURLConnectionDelegate's -connection:willSendRequestForAuthenticationChallenge: using itself as the root cert. I'm not 100% sure I'm doing it right, but looking at the ridiculously, excessively complicated example code, I've come up with this:
    >
    > Ugh, yeah, I’ve had trouble with this too, but it was long enough ago that I don’t remember the details.
    > One thing that might help is calling SecTrustGetResult to get detailed info on the results of trust evaluation. The returned structure might have some clues in it.
    >
    > You can also ask for help on the apple-cdsa list (which is for security/crypto related topics).
    >
    > —Jens

    --
    Rick
  • One correction: the code I posted has a minor error, in that it releases the trust before creating the credential. Just move the line that creates the credential (if the trust evaluation returns true) to before the CFRelease(trust).

    On May 17, 2013, at 15:41 , Rick Mann <rmann...> wrote:

    > I figured it out, finally. Because our server has a dynamically-assigned IP address, and no hostname, we couldn't create a cert that matched. By default, iOS and OS X want to verify the hostname. The solution is to create a new SecTrustRef with a policy that does not check the hostname, and evaluate that. The final code is:
    >
    > - (void)
    > connection: (NSURLConnection*) inConnection
    > willSendRequestForAuthenticationChallenge: (NSURLAuthenticationChallenge*) inChallenge
    > {
    > NSLogDebug(@"Connection challenged");
    >
    > //  Build a new trust based on the supplied trust, so that we can set the policy…
    >
    > NSURLProtectionSpace* protectionSpace = inChallenge.protectionSpace;
    > SecTrustRef trust = protectionSpace.serverTrust;
    >
    > CFIndex numCerts = SecTrustGetCertificateCount(trust);
    > NSMutableArray* certs = [NSMutableArray arrayWithCapacity: numCerts];
    > for (CFIndex idx = 0; idx < numCerts; ++idx)
    > {
    > SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, idx);
    > [certs addObject: CFBridgingRelease(cert)];
    > }
    >
    > //  Create a policy that ignores the host name…
    >
    > SecPolicyRef policy = SecPolicyCreateSSL(true, NULL);
    > OSStatus err = SecTrustCreateWithCertificates(CFBridgingRetain(certs), policy, &trust);
    > CFRelease(policy);
    > if (err != noErr)
    > {
    > NSLogDebug(@"Error creating trust: %d", err);
    > [inChallenge.sender cancelAuthenticationChallenge: inChallenge];
    > return;
    > }
    >
    > //  Set the root cert and evaluate the trust…
    >
    > NSArray* rootCerts = @[ CFBridgingRelease(mRootCert) ];
    > err = SecTrustSetAnchorCertificates(trust, CFBridgingRetain(rootCerts));
    > if (err == noErr)
    > {
    > SecTrustResultType trustResult;
    > err = SecTrustEvaluate(trust, &trustResult);
    > CFRelease(trust);
    >
    > bool trusted = err == noErr;
    > trusted = trusted && (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified);
    > if (trusted)
    > {
    > NSURLCredential* credential = [NSURLCredential credentialForTrust: trust];
    > [inChallenge.sender useCredential: credential forAuthenticationChallenge: inChallenge];
    > return;
    > }
    > }
    >
    > //  An error occurred, or we don't trust the cert, so disallow it…
    >
    > [inChallenge.sender cancelAuthenticationChallenge: inChallenge];
    > }
    >
    > On May 16, 2013, at 12:46 , Jens Alfke <jens...> wrote:
    >
    >>
    >> On May 15, 2013, at 9:13 PM, Rick Mann <rmann...> wrote:
    >>
    >>> I'm trying to validate our self-signed certificate in NSURLConnectionDelegate's -connection:willSendRequestForAuthenticationChallenge: using itself as the root cert. I'm not 100% sure I'm doing it right, but looking at the ridiculously, excessively complicated example code, I've come up with this:
    >>
    >> Ugh, yeah, I’ve had trouble with this too, but it was long enough ago that I don’t remember the details.
    >> One thing that might help is calling SecTrustGetResult to get detailed info on the results of trust evaluation. The returned structure might have some clues in it.
    >>
    >> You can also ask for help on the apple-cdsa list (which is for security/crypto related topics).
    >>
    >> —Jens
    >
    >
    > --
    > Rick

    --
    Rick
previous month may 2013 next month
MTWTFSS
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
Go to today