I'm using a UIWebView to load a website in my iOS app. After updating to iOS 9 and building the app accordingly the website still loads fine from my server, but fails to load the css and js files of bootstrap from cdnjs (e.g. https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css).
I assume this is related to the new ATS but so far I failed to figure out why it fails to load files from cdnjs (they support TLS 1.2 and plenty of modern ciphers) and I don't have an El Capitan with nscurl available to test cdnjs (see https://developer.apple.com/library/prerelease/ios/technotes/App-Transport-Security-Technote/).
I ran a nscurl on El Capitan for you. Based on the result the issue is that cdnjs does not suport Forward Secrecy and therefor you'll have to add an exception for cdnjs.cloudflare.com and set NSAppTransportSecurity to false.
Update: The following lines should be enough:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>cdnjs.cloudflare.com</key>
<dict>
<key>NSExceptionRequiresForwardSecrecy</key><false/>
</dict>
</dict>
</dict>
See the log below for more details:
nscurl --ats-diagnostics --verbose https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css
Starting ATS Diagnostics
Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css.
A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error.
================================================================================
Default ATS Secure Connection
---
ATS Default Connection
ATS Dictionary:
{
}
2015-09-29 14:59:16.983 nscurl[490:10186] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrust 0x7f7f89e052f0 [0x7fff7c50f890]>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<SecCertificate 0x7f7f89e04570 [0x7fff7c50f890]>",
"<SecCertificate 0x7f7f89e04950 [0x7fff7c50f890]>"
), NSUnderlyingError=0x7f7f8b8059b0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrust 0x7f7f89e052f0 [0x7fff7c50f890]>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<SecCertificate 0x7f7f89e04570 [0x7fff7c50f890]>",
"<SecCertificate 0x7f7f89e04950 [0x7fff7c50f890]>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorFailingURLStringKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorClientCertificateStateKey=0}
---
================================================================================
Allowing Arbitrary Loads
---
Allow All Loads
ATS Dictionary:
{
NSAllowsArbitraryLoads = true;
}
Result : PASS
---
================================================================================
Configuring TLS exceptions for cdnjs.cloudflare.com
---
TLSv1.2
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.2";
};
};
}
2015-09-29 14:59:17.140 nscurl[490:10186] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrust 0x7f7f89f136f0 [0x7fff7c50f890]>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<SecCertificate 0x7f7f89f12b20 [0x7fff7c50f890]>",
"<SecCertificate 0x7f7f89f12d60 [0x7fff7c50f890]>"
), NSUnderlyingError=0x7f7f8b804160 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrust 0x7f7f89f136f0 [0x7fff7c50f890]>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<SecCertificate 0x7f7f89f12b20 [0x7fff7c50f890]>",
"<SecCertificate 0x7f7f89f12d60 [0x7fff7c50f890]>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorFailingURLStringKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorClientCertificateStateKey=0}
---
---
TLSv1.1
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.1";
};
};
}
2015-09-29 14:59:17.170 nscurl[490:10186] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrust 0x7f7f89c2a2a0 [0x7fff7c50f890]>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<SecCertificate 0x7f7f89c47750 [0x7fff7c50f890]>",
"<SecCertificate 0x7f7f89c45c90 [0x7fff7c50f890]>"
), NSUnderlyingError=0x7f7f8b9029d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrust 0x7f7f89c2a2a0 [0x7fff7c50f890]>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<SecCertificate 0x7f7f89c47750 [0x7fff7c50f890]>",
"<SecCertificate 0x7f7f89c45c90 [0x7fff7c50f890]>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorFailingURLStringKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorClientCertificateStateKey=0}
---
---
TLSv1.0
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.0";
};
};
}
2015-09-29 14:59:17.206 nscurl[490:10186] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrust 0x7f7f8b905230 [0x7fff7c50f890]>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<SecCertificate 0x7f7f8b904590 [0x7fff7c50f890]>",
"<SecCertificate 0x7f7f8b9047d0 [0x7fff7c50f890]>"
), NSUnderlyingError=0x7f7f89d5fce0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrust 0x7f7f8b905230 [0x7fff7c50f890]>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<SecCertificate 0x7f7f8b904590 [0x7fff7c50f890]>",
"<SecCertificate 0x7f7f8b9047d0 [0x7fff7c50f890]>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorFailingURLStringKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorClientCertificateStateKey=0}
---
================================================================================
Configuring PFS exceptions for cdnjs.cloudflare.com
---
Disabling Perfect Forward Secrecy
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring PFS exceptions and allowing insecure HTTP for cdnjs.cloudflare.com
---
Disabling Perfect Forward Secrecy and Allowing Insecure HTTP
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring TLS exceptions with PFS disabled for cdnjs.cloudflare.com
---
TLSv1.2 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.2";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.1 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.1";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.0 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.0";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring TLS exceptions with PFS disabled and insecure HTTP allowed for cdnjs.cloudflare.com
---
TLSv1.2 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.2";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.1 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.1";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.0 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"cdnjs.cloudflare.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.0";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Related
I tried many options including those provided on stackoverflow and nothing helps. If i don't enter in my application during one hour or more i receive ssl error and my method doesn't execute.
test.getModelFromQuarter { (result) in
if result {
print("*********SUCCESS********")
DispatchQueue.main.async {
self.setChart(dataPoints: self.test.getDataPoint(for: .week), values: self.test.getDefaultCCY(from: .week)!.values)
self.activityIndicator.stopAnimating()
self.currencyBanner.text = "НБУ - \(self.test.getDefaultCCY(from: .week)!.values.last!) \(self.test.getDefaultCCY(from: .week)!.ccy)"
self.setComparingImage(firstValue: (self.test.getDefaultCCY(from: .week)?.values.last)!, secondValue: (self.test.getDefaultCCY(from: .week)?.values.dropLast().last)!)
}
}
This is the method with issue.
func load(url: String, handler: #escaping ([CCYModel]) -> Void) {
var ccyModels: [CCYModel] = []
Alamofire.request(url).responseJSON { (response) in
if response.result.isSuccess {
if let json = try? JSON(response.result.value!) {
if let array = json.array {
for json in array {
let model = CCYModel(ccy: json["cc"].stringValue, rate: json["rate"].doubleValue)
ccyModels.append(model)
}
}
}
} else {
print("*************************")
print("Fetching currencies error \(response.result.error)")
print("*************************")
}
handler(ccyModels)
}
}
This can be fixed by restarting application or by "reloading ui" which means that the data from the site is successfully loaded.
This is my plist where i tried to fix the problem.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsForMedia</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
<key>NSAllowsArbitraryLoadsUsageDescription</key>
<dict/>
<key>NSExceptionDomains</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
<key>https://bank.gov.ua/</key>
<dict/>
</dict>
<key>NSTemporaryExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
It shows the xcode console when bug occurs:
> 2020-01-29 18:07:59.434236+0200 CurrencyInfo[28221:10222320] []
> nw_socket_handle_socket_event [C2.1:3] Socket SO_ERROR [54: Connection
> reset by peer] 2020-01-29 18:07:59.444060+0200
> CurrencyInfo[28221:10222320] Connection 2: received failure
> notification 2020-01-29 18:07:59.444610+0200
> CurrencyInfo[28221:10222320] Connection 2: failed to connect 3:-9816,
> reason -1 2020-01-29 18:07:59.444619+0200 CurrencyInfo[28221:10222320]
> Connection 2: encountered error(3:-9816) 2020-01-29
> 18:07:59.445001+0200 CurrencyInfo[28221:10222320] Task
> <89311F11-5FE1-4BD4-9792-82C796CBE14F>.<2> HTTP load failed, 0/0 bytes
> (error code: -1200 [3:-9816]) 2020-01-29 18:07:59.456604+0200
> CurrencyInfo[28221:10222320] Task
> <89311F11-5FE1-4BD4-9792-82C796CBE14F>.<2> 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={NSErrorFailingURLStringKey=https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?date=20200129&json,
> NSLocalizedRecoverySuggestion=Would you like to connect to the server
> anyway?, _kCFStreamErrorDomainKey=3,
> _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <89311F11-5FE1-4BD4-9792-82C796CBE14F>.<2>,
> _NSURLErrorRelatedURLSessionTaskErrorKey=(
> "LocalDataTask <89311F11-5FE1-4BD4-9792-82C796CBE14F>.<2>" ), NSLocalizedDescription=An SSL error has occurred and a secure
> connection to the server cannot be made.,
> NSErrorFailingURLKey=https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?date=20200129&json,
> NSUnderlyingError=0x2801a03c0 {Error Domain=kCFErrorDomainCFNetwork
> Code=-1200 "(null)"
> UserInfo={_kCFStreamPropertySSLClientCertificateState=0,
> _kCFNetworkCFStreamSSLErrorOriginalValue=-9816, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9816}}, _kCFStreamErrorCodeKey=-9816}
> ************************* Fetching currencies error Optional(Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a
> secure connection to the server cannot be made."
> UserInfo={NSErrorFailingURLStringKey=https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?date=20200129&json,
> NSLocalizedRecoverySuggestion=Would you like to connect to the server
> anyway?, _kCFStreamErrorDomainKey=3,
> _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <89311F11-5FE1-4BD4-9792-82C796CBE14F>.<2>,
> _NSURLErrorRelatedURLSessionTaskErrorKey=(
> "LocalDataTask <89311F11-5FE1-4BD4-9792-82C796CBE14F>.<2>" ), NSLocalizedDescription=An SSL error has occurred and a secure
> connection to the server cannot be made.,
> NSErrorFailingURLKey=https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?date=20200129&json,
> NSUnderlyingError=0x2801a03c0 {Error Domain=kCFErrorDomainCFNetwork
> Code=-1200 "(null)"
> UserInfo={_kCFStreamPropertySSLClientCertificateState=0,
> _kCFNetworkCFStreamSSLErrorOriginalValue=-9816, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9816}}, _kCFStreamErrorCodeKey=-9816})
> *************************
I also checked with nscurl --ats-diagnostics --verbose
> ~ % nscurl --ats-diagnostics --verbose https://bank.gov.ua Starting
> ATS Diagnostics
>
> Configuring ATS Info.plist keys and displaying the result of HTTPS
> loads to https://bank.gov.ua. A test will "PASS" if
> URLSession:task:didCompleteWithError: returns a nil error.
> ================================================================================
>
> Default ATS Secure Connection
> --- ATS Default Connection ATS Dictionary: { } Result : PASS
> ---
>
> ================================================================================
>
> Allowing Arbitrary Loads
>
> --- Allow All Loads ATS Dictionary: {
> NSAllowsArbitraryLoads = true; } Result : PASS
> ---
>
> ================================================================================
>
> Configuring TLS exceptions for bank.gov.ua
>
> --- TLSv1.3 ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionMinimumTLSVersion = "TLSv1.3";
> };
> }; } Result : FAIL Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the
> server cannot be made."
> UserInfo={NSErrorFailingURLStringKey=https://bank.gov.ua/,
> NSLocalizedRecoverySuggestion=Would you like to connect to the server
> anyway?, _kCFStreamErrorDomainKey=3,
> _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <1BDB6BF8-A49A-44D9-93B9-A28E86DF7CA9>.<1>,
> _NSURLErrorRelatedURLSessionTaskErrorKey=(
> "LocalDataTask <1BDB6BF8-A49A-44D9-93B9-A28E86DF7CA9>.<1>" ), NSLocalizedDescription=An SSL error has occurred and a secure
> connection to the server cannot be made.,
> NSErrorFailingURLKey=https://bank.gov.ua/,
> NSUnderlyingError=0x7f8878428810 {Error Domain=kCFErrorDomainCFNetwork
> Code=-1200 "(null)"
> UserInfo={_kCFStreamPropertySSLClientCertificateState=0,
> _kCFNetworkCFStreamSSLErrorOriginalValue=-9858, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9858}}, _kCFStreamErrorCodeKey=-9858}
> ---
>
> --- TLSv1.2 ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionMinimumTLSVersion = "TLSv1.2";
> };
> }; } Result : PASS
> ---
>
> --- TLSv1.1 ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionMinimumTLSVersion = "TLSv1.1";
> };
> }; } Result : PASS
> ---
>
> --- TLSv1.0 ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionMinimumTLSVersion = "TLSv1.0";
> };
> }; } Result : PASS
> ---
>
> ================================================================================
>
> Configuring PFS exceptions for bank.gov.ua
>
> --- Disabling Perfect Forward Secrecy ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionRequiresForwardSecrecy = false;
> };
> }; } Result : PASS
> ---
>
> ================================================================================
>
> Configuring PFS exceptions and allowing insecure HTTP for bank.gov.ua
>
> --- Disabling Perfect Forward Secrecy and Allowing Insecure HTTP ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionAllowsInsecureHTTPLoads = true;
> NSExceptionRequiresForwardSecrecy = false;
> };
> }; } Result : PASS
> ---
>
> ================================================================================
>
> Configuring TLS exceptions with PFS disabled for bank.gov.ua
>
> --- TLSv1.3 with PFS disabled ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionMinimumTLSVersion = "TLSv1.3";
> NSExceptionRequiresForwardSecrecy = false;
> };
> }; } Result : FAIL Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the
> server cannot be made."
> UserInfo={NSErrorFailingURLStringKey=https://bank.gov.ua/,
> NSLocalizedRecoverySuggestion=Would you like to connect to the server
> anyway?, _kCFStreamErrorDomainKey=3,
> _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <AA01562F-24EA-4DC9-BCF7-6E9D625D9361>.<1>,
> _NSURLErrorRelatedURLSessionTaskErrorKey=(
> "LocalDataTask <AA01562F-24EA-4DC9-BCF7-6E9D625D9361>.<1>" ), NSLocalizedDescription=An SSL error has occurred and a secure
> connection to the server cannot be made.,
> NSErrorFailingURLKey=https://bank.gov.ua/,
> NSUnderlyingError=0x7f887a800470 {Error Domain=kCFErrorDomainCFNetwork
> Code=-1200 "(null)"
> UserInfo={_kCFStreamPropertySSLClientCertificateState=0,
> _kCFNetworkCFStreamSSLErrorOriginalValue=-9858, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9858}}, _kCFStreamErrorCodeKey=-9858}
> ---
>
> --- TLSv1.2 with PFS disabled ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionMinimumTLSVersion = "TLSv1.2";
> NSExceptionRequiresForwardSecrecy = false;
> };
> }; } Result : PASS
> ---
>
> --- TLSv1.1 with PFS disabled ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionMinimumTLSVersion = "TLSv1.1";
> NSExceptionRequiresForwardSecrecy = false;
> };
> }; } Result : PASS
> ---
>
> --- TLSv1.0 with PFS disabled ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionMinimumTLSVersion = "TLSv1.0";
> NSExceptionRequiresForwardSecrecy = false;
> };
> }; } Result : PASS
> ---
>
> ================================================================================
>
> Configuring TLS exceptions with PFS disabled and insecure HTTP allowed
> for bank.gov.ua
>
> --- TLSv1.3 with PFS disabled and insecure HTTP allowed ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionAllowsInsecureHTTPLoads = true;
> NSExceptionMinimumTLSVersion = "TLSv1.3";
> NSExceptionRequiresForwardSecrecy = false;
> };
> }; } Result : FAIL Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the
> server cannot be made."
> UserInfo={NSErrorFailingURLStringKey=https://bank.gov.ua/,
> NSLocalizedRecoverySuggestion=Would you like to connect to the server
> anyway?, _kCFStreamErrorDomainKey=3,
> _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <BDF920C9-E44E-4DCD-BED7-94274E454AF4>.<1>,
> _NSURLErrorRelatedURLSessionTaskErrorKey=(
> "LocalDataTask <BDF920C9-E44E-4DCD-BED7-94274E454AF4>.<1>" ), NSLocalizedDescription=An SSL error has occurred and a secure
> connection to the server cannot be made.,
> NSErrorFailingURLKey=https://bank.gov.ua/,
> NSUnderlyingError=0x7f887a800390 {Error Domain=kCFErrorDomainCFNetwork
> Code=-1200 "(null)"
> UserInfo={_kCFStreamPropertySSLClientCertificateState=0,
> _kCFNetworkCFStreamSSLErrorOriginalValue=-9858, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9858}}, _kCFStreamErrorCodeKey=-9858}
> ---
>
> --- TLSv1.2 with PFS disabled and insecure HTTP allowed ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionAllowsInsecureHTTPLoads = true;
> NSExceptionMinimumTLSVersion = "TLSv1.2";
> NSExceptionRequiresForwardSecrecy = false;
> };
> }; } Result : PASS
> ---
>
> --- TLSv1.1 with PFS disabled and insecure HTTP allowed ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionAllowsInsecureHTTPLoads = true;
> NSExceptionMinimumTLSVersion = "TLSv1.1";
> NSExceptionRequiresForwardSecrecy = false;
> };
> }; } Result : PASS
> ---
>
> --- TLSv1.0 with PFS disabled and insecure HTTP allowed ATS Dictionary: {
> NSExceptionDomains = {
> "bank.gov.ua" = {
> NSExceptionAllowsInsecureHTTPLoads = true;
> NSExceptionMinimumTLSVersion = "TLSv1.0";
> NSExceptionRequiresForwardSecrecy = false;
> };
> }; } Result : PASS
> ---
>
> ============================================================================
Is it possible for xcode to ignoring this error? Because my app working fine, data is loaded. Just method in viewDidLoad sometimes doesn't load ui.
I am trying to use Self Signed certificate for my request through Alamofire.
let trustPolicy = ServerTrustPolicy.pinCertificates(certificates:
[certificate], validateCertificateChain: true, validateHost: true)
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"https:-domain-name": trustPolicy,"domain-name" : .disableEvaluation]
let policyManager = ServerTrustPolicyManager(policies: serverTrustPolicies)
But I am getting following error.
CredStore - copyIdentPrefs - Error copying Identity cred.
Error=-25300, query={
class = idnt;
labl = "https://domain-name:443/";
"r_Ref" = 1; }
and
[BoringSSL] boringssl_context_alert_callback_handler(3724)
[C1.1:2][0x139d1bd20] Alert level: fatal, description: handshake
failure 2019-01-22 15:34:23.448605+0530 DB[1276:264543]
[BoringSSL] boringssl_session_errorlog(224) [C1.1:2][0x139d1bd20]
[boringssl_session_handshake_incomplete] SSL_ERROR_SSL(1): operation
failed within the library
2019-01-22 15:34:23.448796+0530
DB[1276:264543] [BoringSSL]
boringssl_session_handshake_error_print(205) [C1.1:2][0x139d1bd20]
5266093016:error:10000410:SSL
routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE:/BuildRoot/Library/Caches/com.apple.xbs/Sources/boringssl/boringssl-109.230.1/ssl/tls_record.cc:586:SSL
alert number 40
2019-01-22 15:34:23.448986+0530 DB[1276:264543]
[BoringSSL] boringssl_context_get_error_code(3545)
[C1.1:2][0x139d1bd20] SSL_AD_HANDSHAKE_FAILURE
2019-01-22
15:34:23.464957+0530 DB[1276:264543] TIC Read Status
[1:0x281599800]: 1:-9824
2019-01-22 15:34:23.467598+0530
DB[1276:264543] Task <43E199F9-B030-4BFD-B9E0-8C9F59B43E72>.<1>
HTTP load failed (error code: -9824 [1:-9824])
2019-01-22
15:34:23.468019+0530 DB[1276:264574] Task
<43E199F9-B030-4BFD-B9E0-8C9F59B43E72>.<1> finished with error - code:
-9824 2019-01-22 15:34:23.470149+0530 DB[1276:264574] Task <43E199F9-B030-4BFD-B9E0-8C9F59B43E72>.<1> load failed with error
Error Domain=NSPOSIXErrorDomain Code=-9824 "Unknown error: -9824"
UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask
<43E199F9-B030-4BFD-B9E0-8C9F59B43E72>.<1>,
_kCFStreamErrorDomainKey=1, NSErrorPeerAddressKey={length = 16, capacity = 16, bytes =
0x100201bb03106e120000000000000000}, _kCFStreamErrorCodeKey=-9824,
_NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <43E199F9-B030-4BFD-B9E0-8C9F59B43E72>.<1>" )} [-9824]
Have been trying this from past 1 week. Any help would be appreciated.
Disabling evaluation in Alamofire doesn't disable the system's base verification of certificates which blocks self-signed certificates by default. I suggest you read Apple's ATS documentation, but I'm guessing this will require some combination of NSExceptionAllowsInsecureHTTPLoads for your domain and other settings. You should also make sure not to ship those settings if you can help it.
So I know the ATS stuff and how to edit the info.plist to allow HTTP. However, the URL is https://api.map.baidu.com/api?v=2. 0&ak=1XjLLEhZhQNUzd93EjU5nOGQ&s=1, which is a HTTPS request, but I still get
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Then I add setenv("CFNETWORK_DIAGNOSTICS", "3", 1); in didFinishLaunchingWithOptions to enable verbose log.
In the log, I find the error log:
5510 Jan 14 10:52:01 MCompass[8549] <Notice>: CFNetwork Diagnostics [3:363] 10:52:01.458 {
5511 Response Error
5512 Request: <CFURLRequest 0x7fecf3cddcb0 [0x10aff37b0]> {url = https://api.map.baidu.com/api?v=2.0&ak=1XjLLEhZhQNUzd93EjU5nOGQ&s=1, cs = 0x0}
5513 Error: Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x7fecf406bbf0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=<CFArray 0x7fecf406cda0 [0x10aff37b0]>{type = immutable, count = 3, values = (
5514 0 : <cert(0x7fecf3fa80e0) s: baidu.com i: VeriSign Class 3 International Server CA - G3>
5515 1 : <cert(0x7fecf3fa8920) s: VeriSign Class 3 International Server CA - G3 i: VeriSign Class 3 Public Primary Certification Authority - G5>
5516 2 : <cert(0x7fecf4069fd0) s: VeriSign Class 3 Public Primary Certification Authority - G5 i: Class 3 Public Primary Certification Authority>
5517 )}}
5518 } [3:363]
5519 Jan 14 10:52:01 MCompass[8549] <Notice>: CFNetwork Diagnostics [3:364] 10:52:01.459 {
5520 Did Fail
5521 Loader: <CFMutableURLRequest 0x7fecf3cdd9f0 [0x10aff37b0]> {url = https://api.map.baidu.com/api?v=2.0&ak=1XjLLEhZhQNUzd93EjU5nOGQ&s=1, cs = 0x0}
5522 Error: Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x7fecf406bbf0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=<CFArray 0x7fecf406cda0 [0x10aff37b0]>{type = immutable, count = 3, values = (
5523 0 : <cert(0x7fecf3fa80e0) s: baidu.com i: VeriSign Class 3 International Server CA - G3>
5524 1 : <cert(0x7fecf3fa8920) s: VeriSign Class 3 International Server CA - G3 i: VeriSign Class 3 Public Primary Certification Authority - G5>
5525 2 : <cert(0x7fecf4069fd0) s: VeriSign Class 3 Public Primary Certification Authority - G5 i: Class 3 Public Primary Certification Authority>
5526 )}}
5527 init to origin load: 0.00280595s
5528 total time: 0.447458s
5529 total bytes: 0
5530 } [3:364]
I am confused, because it's HTTPS request, but still have the issue. I tried the URL on Chrome, it is returning a valid cert (I have the cert knowledge like X509). But cannot figure out why it is blocked.
Could someone help? Thank in advance. Add this domain into ATS exceptions will help, but I don't want to add it, because it's HTTPS already!
UPDATE:
Running
/usr/bin/nscurl --ats-diagnostics -v "https://api.map.baidu.com/api?v=2.0&ak=1XjLLEhZhQNUzd93EjU5nOGQ&s=1"
Will return ALL PASS:
Xuans-MacBook-Pro:~ xuan$ /usr/bin/nscurl --ats-diagnostics -v "https://api.map.baidu.com/api?v=2.0&ak=1XjLLEhZhQNUzd93EjU5nOGQ&s=1"
Starting ATS Diagnostics
Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https://api.map.baidu.com/api?v=2.0&ak=1XjLLEhZhQNUzd93EjU5nOGQ&s=1.
A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error.
================================================================================
Default ATS Secure Connection
---
ATS Default Connection
ATS Dictionary:
{
}
Result : PASS
---
================================================================================
Allowing Arbitrary Loads
---
Allow All Loads
ATS Dictionary:
{
NSAllowsArbitraryLoads = true;
}
Result : PASS
---
================================================================================
Configuring TLS exceptions for api.map.baidu.com
---
TLSv1.2
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.2";
};
};
}
Result : PASS
---
---
TLSv1.1
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.1";
};
};
}
Result : PASS
---
---
TLSv1.0
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.0";
};
};
}
Result : PASS
---
================================================================================
Configuring PFS exceptions for api.map.baidu.com
---
Disabling Perfect Forward Secrecy
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring PFS exceptions and allowing insecure HTTP for api.map.baidu.com
---
Disabling Perfect Forward Secrecy and Allowing Insecure HTTP
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring TLS exceptions with PFS disabled for api.map.baidu.com
---
TLSv1.2 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.2";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.1 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.1";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.0 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.0";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring TLS exceptions with PFS disabled and insecure HTTP allowed for api.map.baidu.com
---
TLSv1.2 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.2";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.1 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.1";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.0 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"api.map.baidu.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.0";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
As discussed on this answer, by simply accessing your API url by HTTPS does not mean it will comply with Apple's ATS. I also use nscurl, but I believe the tool has not yet matured enough and may be quite inefficient at times.
SSL Labs test is far more better and detailed, imho. It will help you track down what's lacking in your SSL configuration.
Note that ATS requires TLS 1.2 at minimum and Perfect Forward Secrecy cipher suites.
I don't know if this would fix your problem, but I had a similar issue recently. In my case, I also had a server which passed nscurl --ats-diagnostics with a PASS on every one, but failed with an ATS -9802 error in the app. The servers used TLS version 1.2, had forward secrecy, used good cipher suites, and had a SHA256 cert.
The SSL Labs page had the hint which pointed to the answer -- it said everything was good, but the SSL chain was incomplete. The server was slightly misconfigured, as it was providing the correct cert, but not an intermediate cert it needed to connect to the root cert (which should already be on the client). There were pointers to where the intermediate cert could be downloaded, so the SSL Labs page did that, and only downgraded the grade to a "B" as a result. But, that means a client implementation also needs to be able to download intermediate certs on its own -- not all implementations do.
In my case, because this was in a testing/development environment where we had issues with other servers sporadically being misconfigured, we had set the AFSecurityPolicy property validatesDomainName to NO, as that got around these other issues (it is of course YES in production). But, that also became the setting for this server as well, which was not strictly necessary. That in turn means that AFSecurityPolicy uses SecPolicyCreateBasicX509() instead of SecPolicyCreateSSL() when it configures the SecTrustRef. That is mostly OK, except the header documentation for SecTrustGetNetworkFetchAllowed() states:
By default, network fetch of missing certificates is enabled if
the trust evaluation includes the SSL policy, otherwise it is disabled.
So, that was the problem. nscurl will use the SSL policy, so it would download the intermediate certificate and work fine. But, with that flag being turned off, ATS would fail at runtime, as SecTrustEvaluate() would return kSecTrustResultRecoverableTrustFailure, which without further intervention will be deemed a failure. If I set validatesDomainName back to YES, then it started working (on this server). Or, if you have a handle on the SecTrustRef after the policy gets added to it, you can call
SecTrustSetNetworkFetchAllowed(trustRef, true);
as that will also allow App Transport Security to fetch the intermediate certificate even with the X509 policy. Or, you can fix the server configuration to provide the entire certificate chain up to but not including the root certificate, like it is supposed to.
EDIT: The SecTrustSetNetworkFetchAllowed call only works on iOS10. For iOS9, I had to call SecTrustSetPolicies() with an SSL policy, i.e. reset the policy list -- that was the only way that the missing certificates would be fetched by ATS on iOS9.
Our app is working fine on both our production and development environments but on our staging environment we get the common error:
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
From my understanding this error occurs when you try to connect to a non https URL.
We use ngrok for our local tunnel which has a https url and works fine.
For production we also use https://ourdomain.com and it works fine.
For staging we use https://staging.ourdomain.comand the error occurs.
I've seen many solutions stating to do this:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
But my employer is against the idea of disabling ATS just to get staging to work, he is also against the idea of adding an exception for our staging URL.
Any ideas why our staging URL would be causing this error to throw or how to fix it?
ATS Diagnostic output:
Starting ATS Diagnostics
Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https://staging.domain.co.
A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error.
================================================================================
Default ATS Secure Connection
---
ATS Default Connection
ATS Dictionary:
{
}
Result : PASS
---
================================================================================
Allowing Arbitrary Loads
---
Allow All Loads
ATS Dictionary:
{
NSAllowsArbitraryLoads = true;
}
Result : PASS
---
================================================================================
Configuring TLS exceptions for staging.domain.co
---
TLSv1.2
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionMinimumTLSVersion = "TLSv1.2";
};
};
}
Result : PASS
---
---
TLSv1.1
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionMinimumTLSVersion = "TLSv1.1";
};
};
}
Result : PASS
---
---
TLSv1.0
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionMinimumTLSVersion = "TLSv1.0";
};
};
}
Result : PASS
---
================================================================================
Configuring PFS exceptions for staging.domain.co
---
Disabling Perfect Forward Secrecy
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring PFS exceptions and allowing insecure HTTP for staging.domain.co
---
Disabling Perfect Forward Secrecy and Allowing Insecure HTTP
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring TLS exceptions with PFS disabled for staging.domain.co
---
TLSv1.2 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionMinimumTLSVersion = "TLSv1.2";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.1 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionMinimumTLSVersion = "TLSv1.1";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.0 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionMinimumTLSVersion = "TLSv1.0";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring TLS exceptions with PFS disabled and insecure HTTP allowed for staging.domain.co
---
TLSv1.2 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.2";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.1 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.1";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.0 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"staging.domain.co" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.0";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
App Transport Security is not just HTTP vs HTTPS. You need to be using properly configured servers+certificates to avoid an ATS issue. From the Apple docs [1]:
The server must support at least Transport Layer Security (TLS)
protocol version 1.2. Connection ciphers are limited to those that
provide forward secrecy. Certificates must be signed using a SHA256 or
greater signature hash algorithm, with either a 2048 bit or greater
RSA key or a 256 bit or greater Elliptic-Curve (ECC) key. Invalid
certificates result in a hard failure and no connection.
If you're on OS X 10.11 (or later), you can use nscurl to troubleshoot. Pop open a terminal and run this:
/usr/bin/nscurl --ats-diagnostics https://staging.ourdomain.com
[1] https://developer.apple.com/library/ios/technotes/App-Transport-Security-Technote/index.html
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 5 years ago.
Improve this question
I want to make an HTTPS request to custom server with self-signed certificate. I'm using NSURLConnection class and processing authentication challenges, but always receive error message in a console:
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
then method "connection:didFailWithError:" gets called with the following error:
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x150094100>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=<CFArray 0x1500ddd90 [0x19f6dab68]>{type = immutable, count = 1, values = (
0 : <cert(0x14e6fb370) s: (server certificate name) i: (custom CA name)>
)}, NSUnderlyingError=0x1504ae170 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSErrorFailingURLStringKey=https://217.92.80.156:9090/(method name and parameters), NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, kCFStreamPropertySSLPeerCertificates=<CFArray 0x1500ddd90 [0x19f6dab68]>{type = immutable, count = 1, values = (
0 : <cert(0x14e6fb370) s: (server certificate name) i: (custom CA name)>
)}, _kCFStreamPropertySSLClientCertificateState=2, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x150094100>, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., _kCFStreamPropertySSLClientCertificates=<CFArray 0x14e5ee8e0 [0x19f6dab68]>{type = mutable-small, count = 2, values = (
0 : <SecIdentityRef: 0x15012cd40>
1 : <cert(0x15014aa70) s: (client certificate name) i: (custom CA name)>
)}, _kCFStreamErrorDomainKey=3, NSErrorFailingURLKey=https://217.92.80.156:9090/(method name and parameters), _kCFStreamErrorCodeKey=-9802}}, NSErrorClientCertificateChainKey=<CFArray 0x14e5ee8e0 [0x19f6dab68]>{type = mutable-small, count = 2, values = (
0 : <SecIdentityRef: 0x15012cd40>
1 : <cert(0x15014aa70) s: (client certificate name) i: (custom CA name)>
)}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://217.92.80.156:9090/(method name and parameters), NSErrorFailingURLStringKey=https://217.92.80.156:9090/(method name and parameters), NSErrorClientCertificateStateKey=2}
App receives two authentication challenges (NSURLAuthenticationMethodClientCertificate and NSURLAuthenticationMethodServerTrust) and processes them in a following manner:
- (void) connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if(challenge.proposedCredential && !challenge.error)
{
[challenge.sender useCredential:challenge.proposedCredential forAuthenticationChallenge:challenge];
return;
}
NSString *strAuthenticationMethod = challenge.protectionSpace.authenticationMethod;
NSLog(#"authentication method: %#", strAuthenticationMethod);
NSURLCredential *credential = nil;
if([strAuthenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate])
{
// get identity and certificate from p.12
NSData *PKCS12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"client" ofType:#"p12"]];
NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:#"password" forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef)PKCS12Data,(__bridge CFDictionaryRef)optionsDictionary, &items);
SecIdentityRef identity = NULL;
SecCertificateRef certificate = NULL;
if(securityError == errSecSuccess)
{
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
identity = (SecIdentityRef)CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
CFArrayRef array = (CFArrayRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemCertChain);
certificate = (SecCertificateRef)CFArrayGetValueAtIndex(array, 0);
}
credential = [NSURLCredential credentialWithIdentity:identity certificates:[NSArray arrayWithObject:(__bridge id)(certificate)] persistence:NSURLCredentialPersistenceNone];
CFRelease(items);
}
else if([strAuthenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
int trustCertificateCount = (int)SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust);
NSMutableArray *trustCertificates = [[NSMutableArray alloc] initWithCapacity:trustCertificateCount];
for(int i = 0; i < trustCertificateCount; i ++)
{
SecCertificateRef trustCertificate = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, i);
[trustCertificates addObject:(__bridge id) trustCertificate];
}
SecPolicyRef policyRef = NULL;
policyRef = SecPolicyCreateSSL(YES, (__bridge CFStringRef) challenge.protectionSpace.host);
SecTrustRef trustRef = NULL;
if(policyRef)
{
SecTrustCreateWithCertificates((__bridge CFArrayRef) trustCertificates, policyRef, &trustRef);
CFRelease(policyRef);
}
if(trustRef)
{
// SecTrustSetAnchorCertificates(trustRef, (__bridge CFArrayRef) [NSArray array]);
// SecTrustSetAnchorCertificatesOnly(trustRef, NO);
SecTrustResultType result;
OSStatus trustEvalStatus = SecTrustEvaluate(trustRef, &result);
if(trustEvalStatus == errSecSuccess)
{
// just temporary attempt to make it working.
// i hope, there is no such problem, when we have final working version of certificates.
if(result == kSecTrustResultRecoverableTrustFailure)
{
CFDataRef errDataRef = SecTrustCopyExceptions(trustRef);
SecTrustSetExceptions(trustRef, errDataRef);
SecTrustEvaluate(trustRef, &result);
}
if(result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)
credential = [NSURLCredential credentialForTrust:trustRef];
}
CFRelease(trustRef);
}
}
else
{
DDLogWarn(#"Unexpected authentication method. Cancelling authentication ...");
[challenge.sender cancelAuthenticationChallenge:challenge];
}
if(credential)
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
else
[challenge.sender cancelAuthenticationChallenge:challenge];
}
In CFNetwork diagnostic log i can see that Handshake procedure is about to be started. At least app sends "ClientHello" message, then server sends its "ServerHello" message and requires for authentication. And here app tries to send authentication response, but immediately receives error. (At the same time, in server logs i don't see any messages about handshake at all). Here is part of diagnostic log:
Sep 15 10:51:49 AppName[331] <Notice>: CFNetwork Diagnostics [3:49] 10:51:49.185 {
Authentication Challenge
Loader: <CFURLRequest 0x1501931c0 [0x19f6dab68]> {url = https://217.92.80.156:9090/(method name and parameters), cs = 0x0}
Challenge: challenge space https://217.92.80.156:9090/, ServerTrustEvaluationRequested (Hash f9810ad8165b3620)
} [3:49]
Sep 15 10:51:49 AppName[331] <Notice>: CFNetwork Diagnostics [3:50] 10:51:49.189 {
Use Credential
Loader: <CFURLRequest 0x1501931c0 [0x19f6dab68]> {url = https://217.92.80.156:9090/(method name and parameters), cs = 0x0}
Credential: Name: server, Persistence: session
} [3:50]
Sep 15 10:51:49 AppName[331] <Notice>: CFNetwork Diagnostics [3:51] 10:51:49.190 {
touchConnection
Loader: <CFURLRequest 0x1501931c0 [0x19f6dab68]> {url = https://217.92.80.156:9090/(method name and parameters), cs = 0x0}
Timeout Interval: 60.000 seconds
} [3:51]
Sep 15 10:51:49 AppName[331] <Notice>: CFNetwork Diagnostics [3:52] 10:51:49.192 {
Response Error
Request: <CFURLRequest 0x14e5d02a0 [0x19f6dab68]> {url = https://217.92.80.156:9090/(method name and parameters), cs = 0x0}
Error: Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFNetworkCFStreamSSLErrorOriginalValue=-9802, kCFStreamPropertySSLPeerCertificates=<CFArray 0x1500ddd90 [0x19f6dab68]>{type = immutable, count = 1, values = (
0 : <cert(0x14e6fb370) s: (server certificate name) i: (custom CA name)>
)}, _kCFStreamPropertySSLClientCertificateState=2, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x150094100>, _kCFStreamPropertySSLClientCertificates=<CFArray 0x14e5ee8e0 [0x19f6dab68]>{type = mutable-small, count = 2, values = (
0 : <SecIdentityRef: 0x15012cd40>
1 : <cert(0x15014aa70) s: (client certificate name) i: (custom CA name)>
)}, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802}
} [3:52]
Our back-end instance can be installed on customer side, so i can't set any domain exception in Info.plist file. Also app may request server by IP address in IPv4 form, but not by domain name (as it is in my example).
What have i tried:
used NSURLSession instead of NSURLConnection, but without any success;
checked Apple's ATS requirements for server implementation here (back-end developer is sure that his implementation meets all of them);
played with setting anchor certificates for trust validation in accordance with various solved issues from stackoverflow and Apple's developer forums;
paid attention particularly to similar post and its related solution at developer forums;
I'm testing https request on iPad Air 2 with iOS 9 GM Seed (Build 13A340) and xCode 7 GM Seed (Build 7A218). Important note: this functionality works fine with iOS 8. Taking that into account i may assume, that problem is in our server, but our back-end developer assured me that there everything is fine.
Now i'm out of ideas. I would appreciate if anyone can give me a hint, or at least suggest some other diagnostic, which would reveal particular error, more specific than "fatal alert".
Thanks.
EDIT 1: SecTrustEvaluate always returns kSecTrustResultRecoverableTrustFailure, that is why i had to find some kind of workaround.
According to this: https://forums.developer.apple.com/message/36842#36842
The best approach to fix HTTP load failed (kCFStreamErrorDomainSSL, -9802) is to set an exception in the info.plist file as follows:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>test.testdomain.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
The important point is that this is not less secure than iOS8, just not as secure as full ATS supported by iOS9.
Have you used nscurl to diagnose the connection issue? If you have a Mac running OS X v10.11 you can run something like this:
/usr/bin/nscurl --ats-diagnostics https://www.yourdomain.com
Alternatively, if you don't have 10.11, you can download the sample code here: https://developer.apple.com/library/mac/samplecode/SC1236/ and build it with XCode and run it like this (changing the path as appropriate for your machine):
/Users/somebody/Library/Developer/Xcode/DerivedData/TLSTool-hjuytnjaqebcfradighsrffxxyzq/Build/Products/Debug/TLSTool s_client -connect www.yourdomain.com:443
(To find the full path for the above, after you've built, open the Products group in your Project Navigator, right click on TLSTool, and "Show in Finder".)
You already linked to Apple's technote on this subject, https://developer.apple.com/library/prerelease/ios/technotes/App-Transport-Security-Technote/ but you didn't say if you ran nscurl or not.
I just met the same problem with ur's.Now i fixes it.It is because the tls version and the certificate sign.As the apple's document say below
apple's document
So i do this thing
info.plist setting
and it works
This issue was solved some time ago. It turned out to be invalid self-signed certificate. It didn't meet all requirements from Apple. Unfortunately i don't know, what exactly it was.