Here's my pretty standard NSURLConnection call to authenticate using a self-signed certificate:
- (SecCertificateRef)certRefFromDerNamed:(NSString*)derFileName resultingDataRef:(CFDataRef*)dataRefPtr{ NSString *thePath = [[NSBundle mainBundle] pathForResource:derFileName ofType:@"der"]; NSData *certData = [[NSData alloc] initWithContentsOfFile:thePath]; CFDataRef certDataRef = (__bridge_retained CFDataRef)certData; SecCertificateRef cert = SecCertificateCreateWithData(NULL, certDataRef); *dataRefPtr = certDataRef; return cert; } - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if (connection == self.connection) { BOOL trusted = NO; if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { SecPolicyRef policyRef = SecPolicyCreateBasicX509(); SecCertificateRef cert1; CFDataRef certData1; cert1 = [self certRefFromDerNamed:@"some3rdpartycacert" resultingDataRef:&certData1]; SecCertificateRef certArray[1] = { cert1 }; CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)certArray, 1, NULL); SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; SecTrustSetAnchorCertificates(serverTrust, certArrayRef); SecTrustResultType trustResult; SecTrustEvaluate(serverTrust, &trustResult); trusted = (trustResult == kSecTrustResultUnspecified); CFRelease(certArrayRef); CFRelease(policyRef); CFRelease(cert1); CFRelease(certData1); } if (trusted) { [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; } else { [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; } } }
And trustResult always kSecTrustResultRecoverableTrustFailure .
The certificate itself is a bit problematic. According to curl cert, the name of the subject on the server does not match the URL to which I am connecting. I contacted this third-party company and they told me that I need to accept this URL mismatch in my code. The problem is that I do not know how to do this on iOS. I can either bypass certificate verification completely (simply assuming trusted=YES and calling useCredential ), or completely fail. The first solution is obviously incorrect from a security point of view and is prone to MITM attacks.
Here's the CURL output (I used the PEM version for the same certificate here):
ukaszs-iMac:Preferences lukasz$ curl --verbose --cacert ~/Desktop/some3rdpartycacert.txt https://dev-service.some3rdparty.com:50101/ * About to connect() to dev-service.some3rdparty.com port 50101 (#0) * Trying XXX.XXX.XXX.XXX... * connected * Connected to dev-service.some3rdparty.com (XXX.XXX.XXX.XXX) port 50101 (#0) * successfully set certificate verify locations: * CAfile: /Users/lukasz/Desktop/some3rdpartycacert.txt CApath: none * SSLv3, TLS handshake, Client hello (1): * SSLv3, TLS handshake, Server hello (2): * SSLv3, TLS handshake, CERT (11): * SSLv3, TLS handshake, Request CERT (13): * SSLv3, TLS handshake, Server finished (14): * SSLv3, TLS handshake, CERT (11): * SSLv3, TLS handshake, Client key exchange (16): * SSLv3, TLS change cipher, Client hello (1): * SSLv3, TLS handshake, Finished (20): * SSLv3, TLS change cipher, Client hello (1): * SSLv3, TLS handshake, Finished (20): * SSL connection using AES256-SHA * Server certificate: * subject: C=CA; ST=Ontario; O=Some 3rdParty Corporation; CN=otherpage.some3rdparty.com; emailAddress=noc@some3rdparty.com * start date: 2013-10-30 16:52:14 GMT * expire date: 2013-10-30 16:52:14 GMT * SSL: certificate subject name 'otherpage.some3rdparty.com' does not match target host name 'dev-service.some3rdparty.com' * Closing connection #0 * SSLv3, TLS alert, Client hello (1): curl: (51) SSL: certificate subject name 'otherpage.some3rdparty.com' does not match target host name 'dev-service.some3rdparty.com'
So how to ignore this particular error in iOS?