Search code examples
iosobjective-cnsurlconnectionnsurlapp-transport-security

iOS Objective C HTTPS request failing


I've searched extensively and have made the necessary changes (so i think) to conform to Appl'es ATS restrictions.

Private key 2048 bits or greater

openssl rsa -in privkey.pem -text -noout

Private-Key: (2048 bit)

Running ssl v1.2 on nginx ssl verified at v1.2

And have even run the make nscurl utility to check the connection, all tests passed.

I also can verify that the server is functioning properly by making a GET on https from the browser and having everything work properly.

My though was that maybe the subdomain is causing an issue, so i updated the info.plist file to the following

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"       "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>boramash.com</key> (also tried gateway.boramash.com)
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
        </dict>
    </dict>
</dict>

With what I believe to be everything working, I get the following errors.

2016-01-25 15:59:17.345 StripePlayground[2999:84984] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802) 2016-01-25 15:59:17.348 StripePlayground[2999:84989] (null) 2016-01-25 15:59:17.348 StripePlayground[2999:84989] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey={type = immutable, count = 1, values = ( 0 : )}, NSUnderlyingError=0x7fd97252e580 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates={type = immutable, count = 1, values = ( 0 : )}}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://gateway.boramash.com/stripe-add-customer, NSErrorFailingURLStringKey= prependingtext_for_stack_overflowhttps://gateway.boramash.com/stripe-add-customer, NSErrorClientCertificateStateKey=0}

Also here is my request making code, pretty basic.

NSString *myrequest = @"https://gateway.boramash.com/stripe-add-customer";

// NSURL *newcustomerURL = [NSURL URLWithString:@"http//45.55.154.107:5050/create-customer"];
NSURL *newcustomerURL = [NSURL URLWithString: myrequest];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: newcustomerURL];
//request.HTTPBody = [[NSString stringWithFormat:@"customer_id=%@&first_name=%@&last_name=%@", testID, firstName, lastName] dataUsingEncoding: NSUTF8StringEncoding ];

request.HTTPMethod = @"GET";

[[[NSURLSession sharedSession] dataTaskWithRequest:request    completionHandler:^(NSData * _Nullable data, NSURLResponse *_Nullable  response, NSError * _Nullable error) {
    //print the result here - new customer has been created!
    NSString *myresponse = [NSString stringWithFormat:@"%@", response];
    NSString *myerror = [NSString stringWithFormat:@"%@", error];

    NSLog(@"%@", myresponse);
    NSLog(@"%@", myerror);
}] resume];

Any advice would be much appreciated!


Solution

  • TL;DR: For some reason, your server is not (always?) sending the intermediate certificate. Check your server configuration, and the certificate/intermediate certificate format (check for errors in your logs, and check that the server was properly restarted).

    You can check on the command line with openssl s_client -connect gateway.boramash.com:443.

    It currently returns:

    depth=0 CN = gateway.boramash.com
    verify error:num=20:unable to get local issuer certificate
    verify return:1
    depth=0 CN = gateway.boramash.com
    verify error:num=21:unable to verify the first certificate
    verify return:1
    ---
    Certificate chain
     0 s:/CN=gateway.boramash.com
       i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
    
    ...
    
        Verify return code: 21 (unable to verify the first certificate)
    

    Which means it can't find a certificate to validate the signature on the certificate.

    You want it to return:

    depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
    verify return:1
    depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X1
    verify return:1
    depth=0 CN = gateway.boramash.com
    verify return:1
    ---
    Certificate chain
     0 s:/CN=gateway.boramash.com
       i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
    
    ...
    
        Verify return code: 0 (ok)
    

    (this was obtained by downloading the intermediate certificate and feeding it to openssl with -CAfile lets-encrypt-x1-cross-signed.pem).

    You can also verify that the intermediate certificate is indeed not sent by adding -showcerts.

    The weird part is that it indeed works (for me) in Safari, though it doesn't work in Firefox. Not quite sure what makes the difference (maybe the intermediate cert was cached from another request to a properly configured server using a certificate from the same CA), but double-check your server configuration (and the format of your certificate file) until openssl likes it, and iOS should like it too.