TLS handshake failed in UIWebView

Within ATS NSAllowsArbitraryLoadsInWebContent = YES, We have a TSL handshake failure when loading a UIWebView to our server in HTTPS, after a NSURLConnection request succeed to the same server in HTTPS.



We think our server is ATS ready. it is well passed by "nscurl --ats-diagnostics --verbose https://test.catchy-creator.me/."

And the server has worked well below iOS 10.2.



After we analyzed the package capture, we saw a weird action from UIWebView.

UIWebView send TLS client hello message with 19 cipher suites which should be accepted.

Cipher Suites (19 suites)

Cipher Suite: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff)

Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)

Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)

Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xc024)

Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xc023)

Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)

Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)

Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)

Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)

Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)

Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)

Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)

Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)

Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)

Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)

Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA256 (0x003d)

Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003c)

Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)

Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)



So the server always selected "Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA256 (0x003d)",

but UIWebView seamed to refuse it and then caused the error.



How does it change from iOS 10.2 about UIWebView? Is it a bug possibly?



If we change NSAllowsArbitraryLoadsInWebContent from YES to NO(strict with ATS), then the UIWebView becomes working well. Because TLS_RSA_WITH_AES_XXXX is no longer in the accepting cipher suite list.



Here is the environment details:

=============

source: https://github.com/czwuwei/ATSTest

device: iPhone7

OS: iOS 10.2

ATS settings in info.plist:

<dict>
  <key>NSAllowsArbitraryLoadsForMedia</key>
  <true/>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
  <key>NSAllowsArbitraryLoadsInWebContent</key>
  <true/>
</dict>


Console Log:

2017-01-19 12:25:52.948244 ATSTest[497:100143] connectionDidFinishLoading https://test.catchy-creator.me/

2017-01-19 12:25:52.948828 ATSTest[497:100176] sleeping...

2017-01-19 12:25:54.951366 ATSTest[497:100176] awake!

2017-01-19 12:25:54.951736 ATSTest[497:100143] load webView

2017-01-19 12:25:55.617687 ATSTest[497:100143] webViewDidStartLoad

2017-01-19 12:25:55.657239 ATSTest[497:100179] [] nw_coretls_read_one_record tls_handshake_process: [-9800]

2017-01-19 12:25:55.673874 ATSTest[497:100175] [] nw_coretls_read_one_record tls_handshake_process: [-9800]

2017-01-19 12:25:55.688000 ATSTest[497:100178] [] nw_coretls_read_one_record tls_handshake_process: [-9824]

2017-01-19 12:25:56.949431 ATSTest[497:100179] [] nw_coretls_read_one_record tls_handshake_process: [-9824]

2017-01-19 12:25:56.954881 ATSTest[497:100195] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)

2017-01-19 12:25:57.000982 ATSTest[497:100143] WebView didFailLoadWithError Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9824, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x17405a730 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSErrorFailingURLStringKey=https://test.catchy-creator.me/, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFNetworkCFStreamSSLErrorOriginalValue=-9824, _kCFStreamPropertySSLClientCertificateState=0, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., _kCFStreamErrorDomainKey=3, NSErrorFailingURLKey=https://test.catchy-creator.me/, _kCFStreamErrorCodeKey=-9824}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://test.catchy-creator.me/, NSErrorFailingURLStringKey=https://test.catchy-creator.me/, _kCFStreamErrorDomainKey=3}

=============

Accepted Reply

I found the point to reproduct the error in my test App is start a NSURLConnection before the UIWebView loading.

Oh, that is interesting. What I suspect’s going on here is that NSURLConnection is putting an entry in the TLS session cache, which is being reused by UIWebView, and hence causing this problem. However, it’s hard to know for sure without a lot more investigation.

Before we go down that path, I have a simple question for you: does the problem reproduce with WKWebView? I suspect it won’t, because WKWebView does its networking in a separate process, which means it doesn’t share the TLS session cache with NSURLConnection.

If WKWebView works then that’s probably the best way forward here. WKWebView is the future of web views on our platforms.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

The change in behaviour of 10.2 is a bug fix (r. 27892687). Prior to that fix,

NSAllowsArbitraryLoadsInWebContent
did not expand the list of cypher suites offered by web view connections, which caused problems for apps connecting to a web server which supports HTTPS but doesn’t support forward secrecy.

As to what’s going wrong with your specific server, I’m not sure. To confirm, the server in question is https://test.catchy-creator.me/, right? I plugged that URL into my web view test project, set set my

Info.plist
entries to match yours, and ran it on 10.2. It displayed that page just fine. A packet trace reveals it negotiated
TLS_RSA_WITH_AES_256_CBC_SHA256
.

Is that server URL correct?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

The server URL is correct, it is just a test URL to reproduct the error.

I have put my test App on https://github.com/czwuwei/ATSTest,

you can use it for reproduction.


I found the point to reproduct the error in my test App is start a NSURLConnection before the UIWebView loading.

Only this case makes the error.

Without the NSURLConnection, just loading a URL in a UIWebView works well.

I found the point to reproduct the error in my test App is start a NSURLConnection before the UIWebView loading.

Oh, that is interesting. What I suspect’s going on here is that NSURLConnection is putting an entry in the TLS session cache, which is being reused by UIWebView, and hence causing this problem. However, it’s hard to know for sure without a lot more investigation.

Before we go down that path, I have a simple question for you: does the problem reproduce with WKWebView? I suspect it won’t, because WKWebView does its networking in a separate process, which means it doesn’t share the TLS session cache with NSURLConnection.

If WKWebView works then that’s probably the best way forward here. WKWebView is the future of web views on our platforms.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for your explanation.


> does the problem reproduce with WKWebView?

No, WKWebView is fine, it doesn't have the problem.


Yes, we have made a plan to replace UIWebView with WKWebView in the near future.

For now we work at the server side, response https with the cipher suites that provide forward secrecy in high priority.

Notice:

Becase my domain "test.catchy-creator.me" expires in 19/2, 2017.

The test app for reproduction will not work from then.