Ian Richard
Ian Richard

Reputation: 525

Manually validate TLS Certificate (ios objective-c)

I am creating an iOS application that communicates with a web server using TLS. When I access the web server, it presents a certificate to my device, and I would like to validate that I can trust it.

I have the root certificate embedded in my xcode project as a der file. The code I have so far gets NSString versions of both certificates in the NSURLConnection's delegate function for authentication challenge.

Any ideas on how to validate manually?

Here's my current code:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSLog(@"Certificate challenge");
if (self.stageNum==0) {
    id <NSURLAuthenticationChallengeSender> sender=challenge.sender;
    NSURLProtectionSpace *protectionSpace=challenge.protectionSpace;
    SecTrustRef trust=[protectionSpace serverTrust];

    //Get server certificate
    SecCertificateRef certificate=SecTrustGetCertificateAtIndex(trust, 0);
    NSData *serverCertificateData=(__bridge NSData *)SecCertificateCopyData(certificate);
    NSString *serverBase64Certificate=[serverCertificateData base64EncodedStringWithOptions:0];

    //Get our certificate
    NSData *certData1 = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"SUMOSRoot" ofType:@"der"]];
    NSString *certBase64=[certData1 base64EncodedStringWithOptions:0];

    //Heres where I need to compare the certificates
    //
    //

    [sender useCredential:[NSURLCredential credentialForTrust:protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
}

Upvotes: 1

Views: 2640

Answers (2)

dgatwood
dgatwood

Reputation: 10407

I'm not sure why you'd want to get an NSString representation. Once you have a SecTrustRef, you can call SecTrustCopyPublicKey to copy the public key to a SecKeyRef, then compare that to the original key data (raw bytes). If the public keys match, you should accept the cert.

This technique is called "certificate pinning", and I'm sure there are probably a number of answers here on Stack Overflow that can provide full code samples if you search for that term.

Ostensibly you could also compare the certs, but that's probably not what you want to do, because certs periodically expire and have to be updated. If you compare the key, then it will continue to work even if they regenerate the cert (assuming they regenerate it with the same private/public key pair).

Or if you are willing to tie it to a particular long-lived private root cert, you can just add that to the list of trusted anchors and reevaluate the trust object, with the caveat that this will break when that private root cert expires. On the other hand, if you pin the key, it will break if you ever have to change the private key for some reason... so it's a tradeoff.

Of course, the best choice in general, if it is possible, is to get a real CA cert and don't mess with any of the chain validation, but I'm assuming that this isn't possible (e.g. because the server is in a .local domain, on a physical device that generates its own cert without Internet access, or some other similar reason). So for things like secure communication with an arbitrary piece of hardware on a .local network, key pinning is the best approach. If the key doesn't match, require the user to add it as a new device or whatever.

Upvotes: 1

Harsh
Harsh

Reputation: 2908

I think this post solves your purpose. Beautifully written/explained.

http://www.techrepublic.com/blog/software-engineer/use-https-certificate-handling-to-protect-your-ios-app/

People who are too lazy to open the links, i'll add some description over here from the post.

enter image description here

Upvotes: 2

Related Questions