We have a server whose cert is signed by our own CA. When handling the TLS challenge, it works fine if our URL uses the hostIP:port. When I instead use the domain:port, the TLS server challenge fails. I can do the same test from curl or an Android client, and do not have the problem. Here is the output from s_client:
This is my URLSession delegate code:
And this is the log file:
The interesting thing is that when I evaluate the trust, SecTrustEvaluateWithError returns true, but when I then call the completion routine with the URLCredential, it errors out.
Code Block openssl s_client -CAfile rootCA -connect test.iwins.kyrio.com:9443 CONNECTED(00000005) depth=2 C = US, ST = Colorado, L = Louisville, O = Kyrio, OU = IWiNS Project, CN = IWINS Server Root CA, emailAddress = iwins@cablelabs.com verify return:1 depth=1 C = US, ST = Colorado, O = Kyrio, OU = IWiNS Project, CN = IWINS Server Intermediate CA, emailAddress = iwins@cablelabs.com verify return:1 depth=0 C = US, ST = Colorado, L = Louisville, O = Kyrio, OU = IWiNS Project, CN = test.iwins.kyrio.com, emailAddress = iwins@cablelabs.com verify return:1 --- Certificate chain 0 s:/C=US/ST=Colorado/L=Louisville/O=Kyrio/OU=IWiNS Project/CN=test.iwins.kyrio.com/emailAddress=iwins@cablelabs.com i:/C=US/ST=Colorado/O=Kyrio/OU=IWiNS Project/CN=IWINS Server Intermediate CA/emailAddress=iwins@cablelabs.com 1 s:/C=US/ST=Colorado/O=Kyrio/OU=IWiNS Project/CN=IWINS Server Intermediate CA/emailAddress=iwins@cablelabs.com i:/C=US/ST=Colorado/L=Louisville/O=Kyrio/OU=IWiNS Project/CN=IWINS Server Root CA/emailAddress=iwins@cablelabs.com --- Server certificate -----BEGIN CERTIFICATE----- gjWbbizE24uVt6PpzoPEP5J/dEDhW/IqlHlQ ---snip--- WWJ6SP5XGUMGLCjJbVuJZhqOCS1uzoflZsQTSsytly8llXAzg9 qZwEhFnrmnbV9eVLmCMquf00/Ao7YrTIEIJhRuHCsWygKTFd7tP+0+xcgn3kkggE 2g/Czfi/UNFGJnm7Vmt2rL7f7sEqueh3+rrSCbv0T1c7jyEmk6tmpMRYjYcfzLgP rp8yQ2HLrj97OTu36T36UKlQmRv9JPk= -----END CERTIFICATE----- subject=/C=US/ST=Colorado/L=Louisville/O=Kyrio/OU=IWiNS Project/CN=test.iwins.kyrio.com/emailAddress=iwins@cablelabs.com issuer=/C=US/ST=Colorado/O=Kyrio/OU=IWiNS Project/CN=IWINS Server Intermediate CA/emailAddress=iwins@cablelabs.com --- Acceptable client certificate CA names /C=US/ST=Colorado/L=Louisville/O=Kyrio/OU=IWiNS Project/CN=IWINS Client Root CA/emailAddress=iwins@cablelabs.com Server Temp Key: ECDH, X25519, 253 bits --- SSL handshake has read 4098 bytes and written 301 bytes --- New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES256-GCM-SHA384 Session-ID: D9974F91E2C92714264C43B63110494FD02AA2B6C384259E0DFC978C1321BA98 Session-ID-ctx: Master-Key: A7543305A36DAD2AC104C2B842462D74E0A151B0DAECB80976E9B5608F8B4B6CEA7026D0BABB339F104B98DC390B62F8 TLS session ticket lifetime hint: 300 (seconds) TLS session ticket: ---snip--- Start Time: 1620870177 Timeout : 7200 (sec) Verify return code: 0 (ok) --- closed
This is my URLSession delegate code:
Code Block public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { // grab the authenticationmethod let methodToAuthenticate = challenge.protectionSpace.authenticationMethod NSLog("TLS Challenge: method to authenticate is: \(methodToAuthenticate)") // authenticate the client credentials once the server trust is verified if methodToAuthenticate == NSURLAuthenticationMethodClientCertificate { NSLog("testing client") completionHandler(.useCredential, verifyClientCredential()) } else if methodToAuthenticate == NSURLAuthenticationMethodServerTrust { NSLog("testing server") let filePath = Utils.RootCertificateUrl().path if let rootData = NSData(contentsOfFile: filePath) { // get contents of file in data format let derDataArray: [Data] = Certificate.parseCertData(rootData as Data) if derDataArray.count == 0 { NSLog("invalid certificate format. Must be PEM") completionHandler(.useCredential, nil) return } var certs: [SecCertificate] = [] // Create two certificates for trust evaluation: client[0] and issuer[1] for certData in derDataArray { guard let cert: SecCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, certData as CFData) else { NSLog("failed to create certificate") completionHandler(.useCredential, nil) return } certs.append(cert) } print("Server Certificates: \(certs)") let trust = challenge.protectionSpace.serverTrust! // create the server trust out of the challenge SecTrustSetAnchorCertificates(trust, certs as CFArray) // set the anchor certificate SecTrustSetAnchorCertificatesOnly(trust, true) // set the anchor cert trust to true var policyArray: CFArray? = nil // create an object to get a hold of the policies SecTrustCopyPolicies(trust, &policyArray) // copy the trust policies in an array var error: CFError? let evaluationSucceeded = SecTrustEvaluateWithError(trust, &error) if !evaluationSucceeded { completionHandler(.rejectProtectionSpace, nil) NSLog("Failed to validate server: \(error!.localizedDescription)") return } completionHandler(.useCredential, URLCredential(trust: trust)) return } else { completionHandler(.rejectProtectionSpace, nil) } } else { completionHandler(.rejectProtectionSpace, nil) } }
And this is the log file:
Code Block 2021-05-12 19:51:38.846606-0600 XCtests-Dummy-Host[26605:23964402] testing server Server Certificates: [<cert(0x7fb68db1f9b0) s: IWINS Server Root CA i: IWINS Server Root CA>] 2021-05-12 19:51:38.858831-0600 XCtests-Dummy-Host[26605:23964399] ATS failed system trust 2021-05-12 19:51:38.858933-0600 XCtests-Dummy-Host[26605:23964399] Connection 3: system TLS Trust evaluation failed(-9802) 2021-05-12 19:51:38.859072-0600 XCtests-Dummy-Host[26605:23964399] Connection 3: TLS Trust encountered error 3:-9802
The interesting thing is that when I evaluate the trust, SecTrustEvaluateWithError returns true, but when I then call the completion routine with the URLCredential, it errors out.