Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS HTTPS requests 101

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

like image 909
Nikolay Dyankov Avatar asked Aug 03 '13 13:08

Nikolay Dyankov


2 Answers

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
like image 84
CouchDeveloper Avatar answered Nov 16 '22 21:11

CouchDeveloper


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

like image 2
Fonix Avatar answered Nov 16 '22 21:11

Fonix