We are trying to implement ssl in our iOS application to connect to Rest webservices running in Tomcat Webserver. Facing following issue when clientAuth is set to true in tomcat configuration :
2018-05-08 09:28:08.442409+0530 [925:337357] XPC connection interrupted
2018-05-08 09:29:26.481465+0530 [925:336959] [Common] _BSMachError: port 9d6f; (os/kern) invalid capability (0x14) "Unable to insert COPY_SEND"
2018-05-08 09:29:26.485693+0530 [925:336959] [Common] _BSMachError: port 9d6f; (os/kern) invalid name (0xf) "Unable to deallocate send right"
2018-05-08 09:29:44.930812+0530 [925:337804] [] nw_coretls_read_one_record tls_handshake_process: [-9825]
2018-05-08 09:29:44.970760+0530 [925:337766] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9825)
2018-05-08 09:29:44.989 [Debug] [main] [HttpUtils.swift:351] handleResponse(response:completionHandler:) > Value: FAILURE;
2018-05-08 09:29:44.999 [Error] [main] [HttpUtils.swift:363] checkRestResponseErrorAndGetUserUnderstandableError(error:completionHandler:) > Error Domain=NSURLErrorDomain Code=-1205 "The server “example.com” did not accept the certificate." UserInfo={NSLocalizedDescription=The server “example.com” did not accept the certificate., _kCFStreamErrorDomainKey=3, NSUnderlyingError=0x16e35930 {Error Domain=kCFErrorDomainCFNetwork Code=-1205 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=1, _kCFNetworkCFStreamSSLErrorOriginalValue=-9825, _kCFStreamErrorCodeKey=-9825, _kCFStreamErrorDomainKey=3, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x16e2ab90>, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x17337200) s: example.com i: Go Daddy Secure Certificate Authority - G2>",
"<cert(0x172e0c00) s: Go Daddy Secure Certificate Authority - G2 i: Go Daddy Root Certificate Authority - G2>",
"<cert(0x172abe00) s: Go Daddy Root Certificate Authority - G2 i: Go Daddy Root Certificate Authority - G2>"
)}}, _kCFStreamErrorCodeKey=-9825, NSErrorFailingURLStringKey=https://example.com:8443/myserver/rest/myresource/servicepath, NSErrorPeerCertificateChainKey=(
"<cert(0x17337200) s: example.com i: Go Daddy Secure Certificate Authority - G2>",
"<cert(0x172e0c00) s: Go Daddy Secure Certificate Authority - G2 i: Go Daddy Root Certificate Authority - G2>",
"<cert(0x172abe00) s: Go Daddy Root Certificate Authority - G2 i: Go Daddy Root Certificate Authority - G2>"
), NSErrorClientCertificateStateKey=1, NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x16e2ab90>, NSErrorFailingURLKey=https://example.com:8443/myserver/rest/myresource/servicepath}
Here is a code snippet that shows how we create AlamoFire Session manager:
import Foundation
import Alamofire
class HttpUtils {
static let utils = HttpUtils()
var alamoFireSSLManager : SessionManager?
public static func getJSONRequestWithBody(url: String, targetViewController: UIViewController?, params : Dictionary<String, String>,
handler: @escaping RiderRideRestClient.responseJSONCompletionHandler){
let isNetworkAvailable = Reachability.isConnectedToNetwork()
if isNetworkAvailable {
utils.createAlamoFireManager()
utils.alamoFireSSLManager!
.request(url, method: .get, parameters: params, encoding: URLEncoding.methodDependent, headers: nil)
.responseJSON(completionHandler: {
(response) in
handleResponse(response: response, completionHandler: handler)
})
}
else {
handler(nil, Errors.NetworkConnectionNotAvailableError)
}
}
func createAlamoFireManager() {
if (HttpUtils.utils.alamoFireSSLManager == nil) {
let mydomainCertificates = getCertificates(filename: "mydomaincertificate", type: "cer")
let mydomainTrustPolicy = ServerTrustPolicy.pinCertificates(
certificates: mydomainCertificates,
validateCertificateChain: true,
validateHost: true)
let sub2DomainCertificates = getCertificates(filename: "sub2domaincertificate", type: "cer")
let sub2DomainTrustPolicy = ServerTrustPolicy.pinCertificates(
certificates: sub2DomainCertificates,
validateCertificateChain: true,
validateHost: true)
var serverTrustPolicies = [String : ServerTrustPolicy] ()
serverTrustPolicies[example.com] = mydomainTrustPolicy
serverTrustPolicies[sub2.example.com] = sub2DomainTrustPolicy
serverTrustPolicies[sub3.example.com] = ServerTrustPolicy.disableEvaluation
HttpUtils.utils.alamoFireSSLManager = SessionManager(configuration: URLSessionConfiguration.default,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
}
}
func getCertificates(filename : String, type : String) -> [SecCertificate] {
let url = Bundle.main.url(forResource: filename, withExtension: type)!
let localCertificate = try! Data(contentsOf: url) as CFData
guard let certificate = SecCertificateCreateWithData(nil, localCertificate)
else { return [] }
return [certificate]
}
}
I tried by adding following to "App Transport Security Settings" of info.plist :
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>sub2.example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
<key>example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
Then I got following error :
2018-05-08 12:36:26.761020+0530[82215:62448515] CredStore - copyIdentPrefs - Error copying Identity cred. Error=-25300, query={
class = idnt;
labl = "https://example.com:8443/";
"r_Ref" = 1;
}
2018-05-08 12:36:26.834384+0530[82215:62448514] [BoringSSL] Function boringssl_session_handshake_error_print: line 3108 boringssl ctx 0x105767600: 4505905920:error:10000412:SSL routines:OPENSSL_internal:SSLV3_ALERT_BAD_CERTIFICATE:/BuildRoot/Library/Caches/com.apple.xbs/Sources/boringssl/boringssl-109.20.5/ssl/tls_record.c:547:SSL alert number 42
2018-05-08 12:36:26.838433+0530[82215:62448514] TIC TCP Conn Failed [1:0x1c017b900]: 3:-9802 Err(-9802)
2018-05-08 12:36:27.044962+0530[82215:62447873] CredStore - copyIdentPrefs - Error copying Identity cred. Error=-25300, query={
class = idnt;
labl = "https://example.com:8443/";
"r_Ref" = 1;
}
2018-05-08 12:36:27.112478+0530[82215:62448515] TIC TCP Conn Failed [2:0x1c417e240]: 3:-9802 Err(-9802)
2018-05-08 12:36:27.179865+0530[82215:62448515] TIC TCP Conn Failed [3:0x1c417dac0]: 3:-9800 Err(-9800)
2018-05-08 12:36:27.180800+0530[82215:62448515] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9800)
2018-05-08 12:36:27.180898+0530[82215:62448515] Task .<1> HTTP load failed (error code: -1200 [3:-9800])
2018-05-08 12:36:27.182153+0530[82215:62448514] Task .<1> finished with error - code: -1200
2018-05-08 12:36:27.204 [Error] [main] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9800, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x1c025d2b0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9800, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9800}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://example.com:8443/myserver/rest/myresource/servicepath, NSErrorFailingURLStringKey=https://example.com:8443/myserver/rest/myresource/servicepath, _kCFStreamErrorDomainKey=3}
I generated the certificate using following command and made sure this certificate is applied to my target in Xcode:
openssl s_client -connect example.com:8443 -servername example.com < /dev/null | openssl x509 -outform DER > mydomaincertificate.cer
I have checked several SO posts to understand what's wrong, but none of the answers given in other posts worked. What am I doing wrong?
Apparently, client certificates are not supported by AlamoFire! Hence this error when clientAuth is set to true in tomcat configuration.