Add self-signed root certificate into iOS app

Hello,

I have a WebSocket server running locally on 127.0.0.1 inside my iOS app using SwiftNIO Transport Services.

This WebSocket server is using a certificate with 127.0.0.1 name signed by a custom root certificate which is self-signed (so not trusted by default).

Inside my app, I open a WKWebView and some JavaScript inside the webpage I navigate to is able to connect to my server with WebSocket secure protocol (wss) to send JSON formatted command to be executed natively in the app and send back a response.

The javascript client library is based on universal-websocket-client npm library which is based on the native WebSocket Web API of the browser. I control also this library.

In order for this to work, I have to install manually the profile of the root certificate and to trust it explicitly in the settings.

I would like to avoid having the user to manually make an action to trust this certificate because perhaps I'd like to put my app at the end in the App Store.

I don't need this certificate to be trusted for the whole device, just for my app.

Is there any option that I have to do so ?

For example by prompting the user inside the app to trust the root or even doing it silently for the user ? Perhaps by adding the custom root CA into the keychain ?

I tried to look at having a certificate signed by a known CA but for 127.0.0.1, I guess it's not possible.

I tried also to look at the trust evaluation but I'm the server, not the client. And on the javascript side, using the native WebSocket API, it seems that I cannot supplement with my custom CA when making the connection or override the trust evaluation. Or can I ?

Thank you in advance for any help.

Christophe

Accepted Reply

Is there any option that I have to do so?

No. WKWebView does, in general, let you override HTTPS server trust evaluation (using webView(_:didReceive:completionHandler:)) but this does not work for WebSocket connections. Personally I consider that to be a bug (r. 25491679).

One easy option here is to use an insecure WebSocket. The traffic is, after all, local, and thus it TLS isn’t really necessary.

The other technique I’ve used in the past is to create a WebSocket emulation layer within my web view that actually does the work using WKScriptMessage.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

Is there any option that I have to do so?

No. WKWebView does, in general, let you override HTTPS server trust evaluation (using webView(_:didReceive:completionHandler:)) but this does not work for WebSocket connections. Personally I consider that to be a bug (r. 25491679).

One easy option here is to use an insecure WebSocket. The traffic is, after all, local, and thus it TLS isn’t really necessary.

The other technique I’ve used in the past is to create a WebSocket emulation layer within my web view that actually does the work using WKScriptMessage.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Thank you for your prompt reply.

Okay, I see, but I can't use insecure WebSocket if I am in the WKWebView with HTTPS because of mixed-content, correct ?

By does the work, you mean using WKScriptMessage to communicate with the javascript from the web page in the web view and communicate with my WebSocket server underneath or completely get rid of the WebSocket server ?

Thanks.
Christophe
Ok, I figured it out.

As I can't use insecure WebSocket inside the WKWebView if I am in HTTPS (what I want), the best option is to get rid completely of the WebSocket server and to use WKScriptMessage instead which allows to send JavaScript to native code in a WKWebView.

Furthermore, we get rid of networking issues we can have with the WebSocket server and since iOS 14, when we are receiving some JavaScript calls from WKWebView, we can also provide a response easily with a reply handler.

Thanks for the help.