iOS HTTPS requests 101

12,037

Solution 1

Getting the corresponding error description may help:

So, first the error domain kCFStreamErrorDomainSSL means that the error code is an SSL error code as defined in Security/SecureTransport.h:

kCFStreamErrorDomainSSL, -9813 means:

errSSLNoRootCert = -9813, /* cert chain not verified by root */

And that simply means, you have no trusted root certificate and the connection fails because of that authentication failure.

Provide a root certificate on the device for the server trust authentication and you are fine.

There are a few approaches to implement server trust authentication with self-signed certificates, the one more secure than the other.

The simplest approach requires a self-signed certificate which is stored in the bundle of the app, then retrieved and simply byte-compared. Here is an example:

Implementing server trust authentication with a self-signed certificate.

These are a must read also: Technical Note TN2232 HTTPS Server Trust Evaluation and Technical Q&A QA1360 Describing the kSecTrustResultUnspecified error.

The more preferred approach is to use a CA (Certificate Authority) which you can be yourself. That is, you create your own CA and your certificates signed with this CA.

The steps are similar:

  1. Bundel the DER file of your CA's root certificate in your app.
  2. Handle the server trust authentication as follows:

    1. get the authentication challenge
    2. retrieve the trust object from the challenge
    3. create a certificate object from the data in your bundle
    4. set the certificate object as an anchor to the trust object using function SecTrustSetAnchorCertificates.
    5. evaluate the trust

Solution 2

not sure if this will actually fix the problem, but it may help. you should be using

– connection:willSendRequestForAuthenticationChallenge:

since the other methods are deprecated. take a look at the overview of the NSURLConnectionDelegate protocol

Share:
12,037

Related videos on Youtube

Nikolay Dyankov
Author by

Nikolay Dyankov

Updated on September 15, 2022

Comments

  • Nikolay Dyankov
    Nikolay Dyankov about 1 year
    NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
    

    Very, very frustrating! I've been pulling my hair for hours with this. I'm using a self-signed certificate on my Linode server. The port is 8000, couldn't get it to work on 443. I don't believe this is the reason though. Here's my code, it's 99% boilerplate:

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.myserver.com:8000/test.json"]];
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
    

    At the bottom:

    #pragma mark NSURLConnectionDelegate
    
    - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
        NSLog(@"protectionSpace: %@", [protectionSpace authenticationMethod]);
    
        // We only know how to handle NTLM authentication.
        if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodNTLM])
            return YES;
    
        // Explicitly reject ServerTrust. This is occasionally sent by IIS.
        if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
            return NO;
    
        return NO;
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
        [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
        NSLog(@"%@", response);
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
        NSLog(@"%@", data);
    }
    
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
        NSLog(@"didFailWithError");
        NSLog([NSString stringWithFormat:@"Connection failed: %@", [error description]]);
    }
    

    OMG HELP!

    UPDATE

    It worked with this delegate method. I'm receiving the response, but there is a problem.

    - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
        [[challenge sender] useCredential:[NSURLCredential
                                           credentialWithUser:@"user"
                                           password:@"password"
                                           persistence:NSURLCredentialPersistencePermanent] forAuthenticationChallenge:challenge];
    
    }
    

    The "user" and "password" that I have provided are completely random and aren't checked by the server. How can I verify the credentials before accepting the connection on my server?

    EDIT: I'm running a Node.js server

  • Fonix
    Fonix over 10 years
    unfortunately im a bit of a noob with this authentication stuff, so im not sure
  • cat
    cat about 10 years
    Here is the URL for decrypting other error codes ... opensource.apple.com/source/libsecurity_ssl/…