Hi,
Background: I am working on an application which supports SAML based login. To support SAML based login we open the IDP url in the WKWebView and the IDP take it from there. The IDP based URLs are mostly HTTPS urls and hence they work fine with ATS for our application.
Problem: But unfortunately today we enouctered the problem where the certificate of the IDP is not supported by iOS. The certificate fulfills all requirements except the "Forward Secrecy" requirement. Currently we have "NSAllowsArbitraryLoads" set to NO in our application. Now when we run the application and hit the IDP URL we get the following error in didFailProvisionalNavigation:
didFailProvisionalNavigation::Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_WKRecoveryAttempterErrorKey=<WKReloadFrameErrorRecoveryAttempter: 0x7f86110935e0>, NSErrorFailingURLStringKey=<URL>, NSErrorFailingURLKey=<URL>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x7f861109d5d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorDomainKey=3, 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., NSErrorFailingURLKey=<URL>, NSErrorFailingURLStringKey=<URL>, _kCFStreamErrorCodeKey=-9824}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made.}
I have removed the URL string here.
To fix this I tried implementing the -(void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *_Nullable))completionHandler method.
Below is the code for the same:
if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
[ActivityIndicatorHelper hideGlobalHud];
NSLog(@"protectionSpace::%@", [challenge protectionSpace].description);
SecTrustRef trust = [[challenge protectionSpace] serverTrust];
if (trust != NULL) {
SecTrustResultType secresult = kSecTrustResultInvalid;
if (SecTrustEvaluate(trust, &secresult) != errSecSuccess) {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
return;
}
NSLog(@"secresult::%u",secresult);
}
completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
The above NSLog on line 18 always prints result as "4" which is kSecTrustResultUnspecified which as per definition is obtained only if the certificate is invalid. But in this case it seems that the certificate is "invalid" because it does not fulfill the "Forward Secrecy" requirement. I was banking on the status to be kSecTrustResultRecoverableTrustFailure so that I can use the above method to present the user with an alert with message that warns them that the certificate is not trusted. If they wish to proceed, I will modify the trust setting and let them go ahead. But if I get kSecTrustResultUnspecified which can also be obtained for valid URLs, how can I differentiate between kSecTrustResultRecoverableTrustFailure andkSecTrustResultUnspecified?
To ensure that "forward secrecy" of the certificate is a problem, I modified the ATS setting for my app to be as below and the IDP page started loading properly:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>main.app.domain</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
<key>idp.vender.domain</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<false/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
</dict>
</dict>
Is the above behavior of obtaining result as kSecTrustResultUnspecified expected? If yes, how do I differentiate between valid certificate and invalid certificate with "forward secrecy" missing? Any solution is highly appreciated.