Re: HTTPS and IP Addresses

I have an iOS 10 (only) app. The app needs to be able to support the use of self-signed certifcates for IP addresses i.e https://192.168.2.1 in a "test" mode. The IP addresses and certifcates are spawned virtual servers and they don't have access to a DNS or a certificate authority, importantly they are for testing only.

When I run the app in "test" mode I get the following error: The certificate for this server is invalid. You might be connecting to a server that is pretending to be "192.168.2.1" which could put your confidential information at riskWhen I connect the app to the same network as the test server and include NSAllowsLocalNetworking, I get the same error. Following is the output of nsurl:

The app needs to be able to support the use of self-signed certifcates for IP addresses i.e https://192.168.2.1 in a "test" mode.

First up, I recommend that you avoid adding a test mode to your app. IMO, there’s a better way to do this:

Create your own custom CA

Use that CA to create a digital identity for your server

Install that CA’s root certificate on your client devices

This has a bunch of advantages:

It’s less code to write; you don’t have to override server trust evaluation on the client, disable ATS, and so on.

Because there’s no test code in your client, there’s no chance of you accidentally shipping an app to customers with that test code enabled.

WARNING Don’t discount this risk. There’s at least one ‘household name’ developer who has had to ship an emergency update to their app because they accidentally shipped it with server trust evaluation disabled. Don’t be that guy!

Similarly, there’s no chance of any App Review hiccoughs because there are no ATS exceptions that you have to remember to remove in the production build.

Your testing is more realistic because it uses exactly the same code paths as your production app.

Also, keep in mind that most server platforms support Bonjour in some form, which allows you to use .local names to talk to the server. And while a real CA won’t issue a certificate for a .local name, your test CA can!

Alternatively, a CA can issue a certificate for an IP address via the Subject Alternate Name extension.

If you ignore the above advice and continue with your current strategy, my first suggestion is that you do your testing over HTTP (rather than HTTPS). Given that your test servers aren’t even close to your real servers, there doesn’t seem to be any drawbacks to use HTTP for this test mode.

Note HTTP requests to an IP address will work without any ATS exceptions on iOS 10. If you also want to test iOS 9, you’ll need to disable ATS entirely by setting NSAllowsArbitraryLoads.

If you want to stick with HTTPS, you’ll have to take two steps to get things working:

Disable ATS — Again, it’s reasonable to do this using NSAllowsArbitraryLoads given that this is a dedicated test setup.

What I would like to understand is for iOS 10 the code and configuration I posted above worked, with iOS 10.2 (14C92) the app now displays TLS errors and the session delegate on NSURLSession does not get invoked. Are you able to explain what may have changed.

Reading the Technote, it appears the simpliest approach is opening Safari in iOS and browing to the local server i.e https://192.168.0.2 and accepting the certifcate. A dialog appears with the options "Continue", "Details" and "Cancel". If I click "Details", "Trust" appears in the top right. My understanding is that if I tap "Trust", the certifciate is now stored on the device but a profile is not created.

- OR -

Should I email the self-signed certificate (.cer) file so that it's accessible on the device i.e. live.com or gmail.com. Open the attachment and follow the process to "Install" the certificate, thus creating a profile. With a profile created my app can perform the desired network operation. I should be able to remove App Transport Security stanza from the info.plist altogether. And no longer need to perform server trust validation for self signed certificates (refer to my sample code in original post). Is this correct approach?

What I would like to understand is for iOS 10 the code and configuration I posted above worked, with iOS 10.2 (14C92) the app now displays TLS errors and the session delegate on NSURLSession does not get invoked.

Which configuration? The one with justNSAllowsArbitraryLoads set? That should completely disable ATS, which means your session delegate callbacks should be called. If they’re not, something else is happening.

Here’s how I tested this:

I created a self-signed TLS server identity and added it to my keychain.

Note If you don’t have such an identity handy, you can create one using the Certificate Assistant feature in Keychain Access.

I ran TLSTool to create a dummy server that uses that identity. I’ve pasted in an example of this below.

I created a dummy app that tries to connect to that server. I’ve pasted the source code in below.

I added a UISwitch controll to toogle the assignement of AllowSelfSignedCertifcate to ignoreSslCerts when the "isOn" property is true.

On the onButtonClick event, regardless if ignoreSslCerts is an instance of AllowSelfSignedCertificate or nil, the delegate does not get invoked. In saying that, the difference is the urlSession signature.

Once I updated my urlSession the function gets invoked . I guess that was the change I was looking for.

Is it your opinion, that I should remove the NSAppTransportSecurity stanza from info.plist and to manually get the self-signed certifcate onto the test device to prevent having to handle URLSessionDelegate?

Is it your opinion, that I should remove the NSAppTransportSecurity stanza from info.plist and to manually get the self-signed certifcate onto the test device to prevent having to handle URLSessionDelegate?

Yes. Writing code to override HTTPS server trust evaluation is extra work and it carries with it significant security riks. If you can avoid doing so, you should.

I have seen many of your topics related to this issue of connecting to the server, but somehow I still don't manage to get it working.

I have a development server, which already has a self-signed certificate, which can be reached either through an URL or locally (when one is in local network) through the IP. I don't know if that is important, but the URL, however, uses a Subdomain and Port.

I have tried to set Allow Arbitrary Loads setting to App Transport Security Settings in my Xcode but it or added the Exception Domains, but nothing seems to work. I always get the same error in the console: NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)

Now, I really don't want to do any hard-coding to bypass these security evaluations, I don't think it's the right way and I am sure, that there should be another solution. One of which, should be this one with the making of certificates.

I already created a custom CA. Based on this documentation, one must then with this CA create digital identities (certificates) for client and the server.

But after this point, I am not exactly sure, what must I do with them and, what should be uploaded where?

You mentioned, that the root certificate of CA should be uploaded on each test device. What should I do with digital identities? I think this part of documentation is missing and I couldn't find it anywhere.

I use that to issue a digital identity for my server, deep-thought.local. TN2326 has the details on doing this as well.

I set up a Mac to act as my server. I set its local DNS name to deep-thought.local via the Sharing preferences panel.

I set up my server software on that Mac.

I install the digital identity from step 2 as the digital identity for my server. How I do this depends on the server I’m using. For example:

When I’m using Apache, I convert the digital identity to a PEM certificate and private key and put those values into the files referenced by SSLCertificateFile and SSLCertificateKeyFile (typically server.crt and server.key, respectively).

For more info on this, you should consult the Apache docs.

When I’m using TLSTool, it can use the digital identity directly from my Mac’s keychain.

IMPORTANT On iOS 10.3 and later you have to explicitly enable the certificate authority in Settings > General > About > Certificate Trust Settings. At some point I will update QA1948 to cover that but that process is more complex than you might think (specifically, QA1948 covers all of our platforms and some of those platforms — yes, I’m looking at you, tvOS! — make this hard).

At this point I can write NSURLSession code that fetches https://deep-thought.local without any HTTPS server trust evaluation overrides.

More Like This

Retrieving data ...

This site contains user submitted content, comments and opinions and is for informational purposes only. Apple disclaims any and all liability for the acts, omissions and conduct of any third parties in connection with or related to your use of the site. All postings and use of the content on this site are subject to the Apple Developer Forums Participation Agreement.