Alamofire https request only works if NSExceptionAllowsInsecureHTTPLoads is set to true - swift

I have developed an app in Xcode10 with Swift (app name: "TerminalsPOC"). I am making an https request to my organization’s internal web api (let's call the url "https://example.com:50001/RESTAdapter/toolbox/getMyData") using Alamofire. I have a class with a class-level variable to reference a session manager:
// Swift code
let serverTrustPolicies: [String: ServerTrustPolicy] = [
“example.com": .pinCertificates(
certificates: ServerTrustPolicy.certificates(in: Bundle(for: type(of: self))),
validateCertificateChain: false,
validateHost: true
)
]
sessionManager = SessionManager(
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)
sessionManager.request(url, method: .get) ...
I have imported the necessary .cer certificate into the app’s bundle. I have left the default ATS settings, but have added an NSExceptionDomain. The relevant info.plist section looks like
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<false/>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
This works so long as the NSExceptionAllowsInsecureHTTPLoads setting is set to true. If I set it to false, the request fails with the message: An SSL error has occurred and a secure connection to the server cannot be made. [-1200]
2018-12-07 11:55:42.122423-0700 TerminalsPOC[27191:371810] ATS failed
system trust 2018-12-07 11:55:42.122530-0700
TerminalsPOC[27191:371810] System Trust failed for [2:0x600001fad740]
2018-12-07 11:55:42.122637-0700 TerminalsPOC[27191:371810] TIC SSL
Trust Error [2:0x600001fad740]: 3:0
2018-12-07 11:55:42.125928-0700
TerminalsPOC[27191:371810] NSURLSession/NSURLConnection HTTP load
failed (kCFStreamErrorDomainSSL, -9802)
2018-12-07 11:55:42.126109-0700 TerminalsPOC[27191:371810] Task
<54567E3C-2BBC-4227-9C0A-FC60370A10AA>.<1> HTTP load failed (error
code: -1200 [3:-9802])
2018-12-07 11:55:42.126872-0700
TerminalsPOC[27191:371812] Task
<54567E3C-2BBC-4227-9C0A-FC60370A10AA>.<1> finished with error - code:
-1200
2018-12-07 11:55:42.140600-0700 TerminalsPOC[27191:371810] Task <54567E3C-2BBC-4227-9C0A-FC60370A10AA>.<1> load failed with error
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred
and a secure connection to the server cannot be made."
UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to
the server anyway?, _kCFStreamErrorDomainKey=3,
NSErrorPeerCertificateChainKey=(
"",
"" ), NSErrorClientCertificateStateKey=0,
NSErrorFailingURLKey=https://example.com:50001/RESTAdapter/toolbox/getMyData,
NSErrorFailingURLStringKey=https://example.com:50001/RESTAdapter/toolbox/getMyData,
NSUnderlyingError=0x6000024e89f0 {Error Domain=kCFErrorDomainCFNetwork
Code=-1200 "(null)"
UserInfo={_kCFStreamPropertySSLClientCertificateState=0,
kCFStreamPropertySSLPeerTrust=,
_kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"",
"" )}}, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <54567E3C-2BBC-4227-9C0A-FC60370A10AA>.<1>" ), _kCFStreamErrorCodeKey=-9802, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <54567E3C-2BBC-4227-9C0A-FC60370A10AA>.<1>,
NSURLErrorFailingURLPeerTrustErrorKey=,
NSLocalizedDescription=An SSL error has occurred and a secure
connection to the server cannot be made.} [-1200]
I tried running “nscurl --ats-diagnostics https://example.com:50001/RESTAdapter/toolbox/getMyData”, and the response included the following:
Default ATS Secure Connection
--- ATS Default Connection Result : PASS
========
Allowing Arbitrary Loads
--- Allow All Loads Result : PASS
========= Configuring TLS exceptions for example.com
--- TLSv1.3 2018-12-07 10:59:17.492 nscurl[24303:331847] NSURLSession/NSURLConnection HTTP load failed
(kCFStreamErrorDomainSSL, -9800) Result : FAIL
--- TLSv1.2 Result : PASS
--- TLSv1.1 Result : PASS
--- TLSv1.0 Result : PASS
============ Configuring PFS exceptions for example.com
--- Disabling Perfect Forward Secrecy Result : PASS
========== Configuring PFS exceptions and allowing insecure HTTP for example.com
--- Disabling Perfect Forward Secrecy and Allowing Insecure HTTP Result : PASS
This all looks OK to me. I must be missing something.
So my questions are:
1. Why does setting the NSExceptionAllowsInsecureHTTPLoads to true cause the call to work, given that it is an https request (with no redirect)? I thought this setting only affects http calls, and should not affect https calls.
2. How can I get this web request to work without setting NSExceptionAllowsInsecureHTTPLoads (which seems to be a hack/work-around, doesn’t it)?

The problem in this case was that the app was running on a simulator on which the required certificate had not been installed.
Once the correct (root) certificate had been installed and trusted, the pinned certificate check passed, and it was then possible to set the NSExceptionAllowsInsecureHTTPLoads info.plist setting back to "NO".
I wish the error message had been more explicit. :-/

Related

default TLS Trust evaluation failed for iOS development with webapis on local machine listening to the IP

I am new to iOS/SwiftUI development but am trying to develop an app and webapis on my MacBook Pro. I am using VSCode and C# on the backend and calling the webapis from the iPhone simulator.
I have a localhost certificate in my Keychain with a Subject Alternative Name of the ip for my local machine (10.0.0.5). I have this working with Android using 10.0.2.2 (emulator localhost for computer hosting the emulator), so I know it does work.
I can also run the API from the browser using 10.0.0.5 and get the results.
When I run from the simulator I get these error:
2022-01-15 18:05:03.352411-0500 TestApp[24757:274897] [Unknown process
name] copy_read_only: vm_copy failed: status 1. calling loadData...
loading data... 2022-01-15 18:05:06.008842-0500 TestApp[24757:274902]
Connection 1: default TLS Trust evaluation failed(-9807) 2022-01-15
18:05:06.009049-0500 TestApp[24757:274902] Connection 1: TLS Trust
encountered error 3:-9807 2022-01-15 18:05:06.009148-0500
TestApp[24757:274902] Connection 1: encountered error(3:-9807)
2022-01-15 18:05:06.010008-0500 TestApp[24757:274902] Task
<17311174-24FF-403A-AA1B-4894D097FB41>.<1> HTTP load failed, 0/0 bytes
(error code: -1202 [3:-9807]) 2022-01-15 18:05:06.013874-0500
TestApp[24757:274896] Task <17311174-24FF-403A-AA1B-4894D097FB41>.<1>
finished with error [-1202] Error Domain=NSURLErrorDomain Code=-1202
"The certificate for this server is invalid. You might be connecting
to a server that is pretending to be “10.0.0.5” which could put your
confidential information at risk."
UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to
the server anyway?, _kCFStreamErrorDomainKey=3,
NSErrorPeerCertificateChainKey=(
"<cert(0x7fbef400f200) s: localhost i: localhost>" ), NSErrorClientCertificateStateKey=0,
NSErrorFailingURLKey=https://10.0.0.5:5001/WeatherForecast,
NSErrorFailingURLStringKey=https://10.0.0.5:5001/WeatherForecast,
NSUnderlyingError=0x6000023c6490 {Error Domain=kCFErrorDomainCFNetwork
Code=-1202 "(null)"
UserInfo={_kCFStreamPropertySSLClientCertificateState=0,
kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x600001c58500>,
_kCFNetworkCFStreamSSLErrorOriginalValue=-9807, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9807, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x7fbef400f200) s: localhost i: localhost>" )}}, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <17311174-24FF-403A-AA1B-4894D097FB41>.<1>" ), _kCFStreamErrorCodeKey=-9807, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <17311174-24FF-403A-AA1B-4894D097FB41>.<1>,
NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x600001c58500>,
NSLocalizedDescription=The certificate for this server is invalid. You
might be connecting to a server that is pretending to be “10.0.0.5”
which could put your confidential information at risk.} Invalid data
I followed the instructions from this post to put the NSExceptionDomain I am using. I am having trouble digesting the Overriding TLS Chain Validation Correctly. I followed this thread on Swift 5.1 UIWebView Does not View URL because of untrusted Certificate as well. Also, iOS app ignoring NSAppTransportSecurity.
My Info.plist has this in it:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>https://10.0.0.5:5001/WeatherForecast</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
I tried a variety of combinations for the key from 10.0.0.5 to https://10.0.0.5:5001/WeatherForecast.
UPDATE
I tried this Domain=NSURLErrorDomain Code 1202 as well, exporting the .cer from keychain and installing it on the emulator, but I still get the same error:

Secure connection can't be made

Using: Alamofire 5, self-signed certificate.
The certificate is stored in application resources
AlamofireExtension(Bundle.main).certificates - returns it.
Session:
private let session: Session = {
let manager = ServerTrustManager(evaluators: ["com.my.host.here": PinnedCertificatesTrustEvaluator(certificates: AlamofireExtension(Bundle.main).certificates, acceptSelfSignedCertificates: true, performDefaultValidation: false, validateHost: false)])
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 60
config.timeoutIntervalForResource = 60
config.httpMaximumConnectionsPerHost = 2
return Session(configuration: config, serverTrustManager: manager)
}()
.plist configuration:
Purpose is to use a self-signed certificate to validate the server without disabling validation and allowing insecure connections.
Note com.my.host.here is not my real hostname, I changed it for privacy reasons.
And here the result:
Task <715E277D-6BAA-4926-91AA-1060903D2924>.<1> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9802])
my_app[12152:5481718] Task <715E277D-6BAA-4926-91AA-1060903D2924>.<1> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
"<cert(0x160039a00) s: com.my.host.here i: com.my.host.here>"
), NSErrorClientCertificateStateKey=0, NSErrorFailingURLKey=https://com.my.host.here:8043/some_end_point_here, NSErrorFailingURLStringKey=https://com.my.host.here:8043/some_end_point_here, NSUnderlyingError=0x281354870 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x282f35320>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x160039a00) s: com.my.host.here i: com.my.host.here>"
)}}, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <715E277D-6BAA-4926-91AA-1060903D2924>.<1>"
), _kCFStreamErrorCodeKey=-9802, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <715E277D-6BAA-4926-91AA-1060903D2924>.<1>, NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x282f35320>, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made.}
Questions is: What is the problem? Is it a problem with the certificate, or with the server side settings? and maybe something else?
You need to change NSExceptionAllowsInsecureHTTPLoads to YES.
NSExceptionAllowsInsecureHTTPLoads
Set the value for this key to YES to allow insecure HTTP loads for the given domain, or to be able
to loosen the server trust evaluation requirements for HTTPS
connections to the domain, as described in Performing Manual Server
Trust Authentication.

Alamofire SSL error on local dev site

I am trying to develop a app on my local machine with the local version of my api, but I am keep getting this error when I run alamofire get request.
[BoringSSL] Function nw_protocol_boringssl_input_finished: line 1436 Peer disconnected during the middle of a handshake. Sending errSSLFatalAlert(-9802) alert
2018-08-13 10:47:23.056793-0500 Edev[6851:453243] TIC TCP Conn Failed [1:0x60c000178480]: 3:-9802 Err(-9802)
2018-08-13 10:47:23.057251-0500 Edev[6851:453243] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
2018-08-13 10:47:23.057432-0500 Edev[6851:453243] Task <97194E8E-7E15-42EC-B8BA-534AC4E313A2>.<1> HTTP load failed (error code: -1200 [3:-9802])
2018-08-13 10:47:23.057686-0500 Edev[6851:453245] Task <97194E8E-7E15-42EC-B8BA-534AC4E313A2>.<1> finished with error - code: -1200
FAILURE: Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9802, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x60400044f570 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://edev.test/edev, NSErrorFailingURLStringKey=https://edev.test/edev, _kCFStreamErrorDomainKey=3}
[BoringSSL] Function nw_protocol_boringssl_input_finished: line 1436 Peer disconnected during the middle of a handshake. Sending errSSLFatalAlert(-9802) alert
2018-08-13 10:47:23.056793-0500 Edev[6851:453243] TIC TCP Conn Failed [1:0x60c000178480]: 3:-9802 Err(-9802)
2018-08-13 10:47:23.057251-0500 Edev[6851:453243] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
2018-08-13 10:47:23.057432-0500 Edev[6851:453243] Task <97194E8E-7E15-42EC-B8BA-534AC4E313A2>.<1> HTTP load failed (error code: -1200 [3:-9802])
2018-08-13 10:47:23.057686-0500 Edev[6851:453245] Task <97194E8E-7E15-42EC-B8BA-534AC4E313A2>.<1> finished with error - code: -1200
FAILURE: Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9802, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x60400044f570 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://edev.test/edev, NSErrorFailingURLStringKey=https://edev.test/edev, _kCFStreamErrorDomainKey=3}
This is the alamofire I am using to fetch the data
Alamofire.request("https://edev.test/edev").responseJSON { response in
print(response)
print("Request: \(String(describing: response.request))") // original url request
print("Response: \(String(describing: response.response))") // http url response
print("Result: \(response.result)") // response serialization result
if let json = response.result.value {
print("JSON: \(json)") // serialized json response
}
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)") // original server data as UTF8 string
}
}
I have have put a domain exception if my plist file that should make this run successfully based on what I have found online, but it still is not working.
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>edev.test</key>
<dict/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
does the server have an SSL certificate? if not change URL to Alamofire.request("http://edev.test/edev").responseJSON { response in

Keep Alive Timer called close() error between jersey servers, using TLS

I have a multimodule Jersey project, where at some point in serving a request a Gateway module will talk to another Gateway module (this is the "service discovery" part of the communication) on another PC. This communication works well, but when the code execution gets to the next request where these 2 modules talk again (this is still for serving that first request, this is the "negotiation" part of the communication between the gateway modules), the request times out fast with a
Caused by: java.net.SocketException: Unexpected end of file from server
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:790)
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:787)
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1534)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1439)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:394)
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:285)
... 38 more
I use the -Djavax.net.debug=SSL VM argument to see what's happening, and both gateways print the same log:
Keep-Alive-Timer, called close()
Keep-Alive-Timer, called closeInternal(true)
Keep-Alive-Timer, SEND TLSv1.2 ALERT: warning, description = close_notify
Keep-Alive-Timer, WRITE: TLSv1.2 Alert, length = 64
Keep-Alive-Timer, called closeSocket(true)
grizzly-nio-kernel(4) SelectorRunner, READ: TLSv1.2 Alert, length = 64
grizzly-nio-kernel(4) SelectorRunner, RECV TLSv1.2 ALERT: warning, close_notify
grizzly-nio-kernel(4) SelectorRunner, closeInboundInternal()
grizzly-nio-kernel(4) SelectorRunner, closeOutboundInternal()
grizzly-nio-kernel(4) SelectorRunner, SEND TLSv1.2 ALERT: warning, description = close_notify
grizzly-nio-kernel(4) SelectorRunner, WRITE: TLSv1.2 Alert, length = 64
This time the code execution does not reach my code on the receiver gateway. Each HTTP request I send in these modules, a new javax.ws.rs.client.Client is built and used. Both gateway requests have the exact same javax.ws.rs.client.Client configuration (same connect/read timeout, same SSL context, same hostnameverifier), only the path and payload is different, I'm not trying to keep connections open at all, since this is a REST application.
I cant find anything useful on the net for Keep-Alive-Timer, called close(), but I assume this is the TLS keep alive function, since this communication works well without TLS, on normal HTTP. What could be the problem? I can provide more detail if needed.

Attempting POST request in Swift 4 gives "TIC SSL Trust Error"

Configuration: developing a macOS application, macOS High Sierra 10.13.1, and Xcode version 9.1 (9B55).
Alright, I've looked all over the place for a possible solution, tried several things, and nothing's worked so far.
I have a Web service running under Spring Boot, so it uses Apache Tomcat underneath and I've configured it to use the last stable release of Java 8 (1.8u152).
Because I configured my service to use HTTPS, I created a self-signed certificate. I've read that Swift doesn't really like this, but there are things you are supposed to be able to do to mitigate that until your app is ready for production.
So I created my certificate using keytool, tested it in the browser, got the usual "Your connection is not private" warning, which I expected since it was self-signed. But after allowing the exception, it works and all HTTP requests redirect to HTTPS like I programmed it to.
Now, when I try to make a POST request in Swift, I get this group of errors:
2017-11-27 22:15:35.963123-0500 MyApp[2885:4510057] TIC SSL Trust Error [1:0x600000168c40]: 3:0
2017-11-27 22:15:35.971297-0500 MyApp[2885:4510057] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
2017-11-27 22:15:35.971344-0500 MyApp[2885:4510057] Task <1ADE665F-B044-4678-8291-BF63E579CCDE>.<1> HTTP load failed (error code: -1202 [3:-9813])
2017-11-27 22:15:35.971456-0500 MyApp[2885:4510056] Task <1ADE665F-B044-4678-8291-BF63E579CCDE>.<1> finished with error - code: -1202
Upon researching this further, I made the following additions to my Info.plist file:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
I tried doing this in raw source code and via the plist editor. No luck either way.
Here is the code that attempts the POST request:
let url = Constants.SERVICE_URL + "account/post"
let body: [String : Any] =
["firstName": txtFirstName.stringValue,
"lastName": txtLastName.stringValue,
"email": txtEmail.stringValue,
"password": txtPassword.stringValue];
let req = Request.create(urlExtension: url, httpVerb: Constants.HTTP_POST, jsonBody: body)
let task = URLSession.shared.dataTask(with: req) { data, response, err in
guard let data = data, err == nil else {
reply(false)
return
}
do {
let resp = try JSONSerialization.jsonObject(with: data)
reply(resp)
} catch {
print("Error: " + error.localizedDescription)
reply(false)
}
}
task.resume()
I'm at a loss now... does anyone know what to do about this?
I fixed it! Since my certificate was self-signed, my Mac did not trust it by default (which makes sense). I followed the instructions at macOS Sierra: If your certificate isn’t being accepted and it worked.
Make sure to add the certificate to the System keychain.