My iOS app communicates with my backend server which has a valid certificate. During development my iOS app is configured to communicate with my internal test server which has a certificate signed by my self signed root certificate.
Prior to iOS 11 I could configure my app to trust connections signed by my self signed root using instructions laid out here: https://developer.apple.com/library/content/technotes/tn2232/_index.html#//apple_ref/doc/uid/DTS40012884-CH1-SECCUSTOMCERT
However in iOS 11 it is no longer possible for an app to bypass ATS without creating an ATS exception int the info.plist. This thread includes some explanation from Apple: https://forums.developer.apple.com/thread/89694
So now in iOS how can I install my custom CA root on all my iOS simulators in some kind of automated way?
Since iOS 11 support multi-app UI testing I created a UI test that causes the iOS system to install my custom CA's root using Safari and the Settings app. The source for the UI test is as follows:
import XCTest
class InstallRootCerts: XCTestCase {
override func setUp() {
super.setUp()
continueAfterFailure = false
XCUIApplication().launch()
}
override func tearDown() {
super.tearDown()
}
func testInstallTestingRootCertOnDevice() {
// Set test time env var ROOT_CA to point to your custom CA's .cer file.
let cert = ProcessInfo.processInfo.environment["ROOT_CA"]
// Set test time env var ROOT_CA_NAME to contain the name of your CA.
let caName = ProcessInfo.processInfo.environment["ROOT_CA_NAME"]
let safari = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari")
let settings = XCUIApplication(bundleIdentifier: "com.apple.Preferences")
safari.activate()
XCTAssertNotNil(cert)
let certUrl = "file://\(cert!)"
if safari.otherElements["URL"].exists {
safari.otherElements["URL"].tap()
}
let addressBar = safari.textFields["URL"]
addressBar.typeText(certUrl)
safari.buttons["Go"].tap()
safari.buttons["Allow"].tap()
XCTAssertTrue( settings.wait(for: .runningForeground, timeout: 30) )
if !settings.staticTexts["Verified"].exists {
settings.buttons["Install"].tap()
settings.buttons["Install"].tap()
settings.sheets.buttons["Install"].tap()
}
// Now trust the root certificate
settings.buttons["Cancel"].tap()
XCTAssertTrue( safari.wait(for: .runningForeground, timeout: 120) )
settings.activate()
XCTAssertTrue( settings.wait(for: .runningForeground, timeout: 120) )
settings.buttons["General"].tap()
settings.cells["About"].tap()
settings.cells["Certificate Trust Settings"].tap()
let cell = settings.cells.containing(.staticText, identifier: caName)
let toggle = cell.switches.firstMatch
if toggle.value as? String != "1" {
toggle.tap()
settings.buttons["Continue"].tap()
}
}
}
This test case only works on simulators running iOS 11.0. I think it could be made to work with actual devices if you put the .cer file on a web server and opened it over http.
This test case can then be run as a Bot on your Xcode server or you can run it on any simulator locally to set it up. This is a hack I know.