SSL Error 1200 when using domain name, but IP works fine

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:
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.

Much more in the log file that I can post, wouldn't fit in the original post.
I'm wondering if there is some low level trace logging I can enable to see WHY the app isn't happy with the cert when using the domain url?

It doesn't look like you're evaluating your server trust against any sort of Policy. Is that intentional? If you are in need of using an IP, I suspect that SecPolicyCreateSSL would always fail, but SecPolicyCreateBasicX509 could be used here instead. If you get stuck here open a TSI and either Quinn or myself can look into this deeper.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Ok, so I'm seeing "MissingIntermediate(leaf)" only when using the domain URL. This is the failure case when using the domain in the url:
Code Block default 09:59:21.541985-0600 XCtests-Dummy-Host Connection 3: enabling TLS
default 09:59:21.542007-0600 XCtests-Dummy-Host Connection 3: starting, TC(0x0)
default 09:59:21.542031-0600 XCtests-Dummy-Host [C3 2B3E4133-E9E7-4AE3-A31B-56C90388F49F test.iwins.kyrio.com:9443 tcp, url hash: 153e2ccd, tls, context: com.apple.CFNetwork.NSURLSession.{91028BB7-8613-4AB0-9E60-9B7553A0DC92}{(null)}{Y}{2}, proc: BEEF2622-1A02-341A-A97E-9F77BBB13DBE] start
default 09:59:21.542071-0600 XCtests-Dummy-Host [C3 test.iwins.kyrio.com:9443 initial path ((null))] event: path:start @0.000s
default 09:59:21.542335-0600 XCtests-Dummy-Host [C3 test.iwins.kyrio.com:9443 waiting path (satisfied (Path is satisfied), interface: en5)] event: path:satisfied @0.000s, uuid: AF5E4168-4F47-4B99-A484-4A081223C92F
default 09:59:21.542639-0600 XCtests-Dummy-Host [C3 test.iwins.kyrio.com:9443 in_progress resolver (satisfied (Path is satisfied), interface: en5)] event: resolver:start_dns @0.000s
default 09:59:21.542685-0600 XCtests-Dummy-Host nw_connection_report_state_with_handler_on_nw_queue [C3] reporting state preparing
default 09:59:21.543358-0600 XCtests-Dummy-Host Task <C39EAD13-8E45-4227-896A-586B12BA7969>.<1> setting up Connection 3
default 09:59:21.544702-0600 XCtests-Dummy-Host [C3 test.iwins.kyrio.com:9443 in_progress resolver (satisfied (Path is satisfied), interface: en5)] event: resolver:receive_dns @0.002s
default 09:59:21.544841-0600 XCtests-Dummy-Host [C3.1 18.210.100.18:9443 initial path ((null))] event: path:start @0.002s
default 09:59:21.545171-0600 XCtests-Dummy-Host [C3.1 18.210.100.18:9443 waiting path (satisfied (Path is satisfied), interface: en5)] event: path:satisfied @0.003s, uuid: E3CD1BD5-256A-4E6E-9E1E-A29F976ACEAD
default 09:59:21.545947-0600 XCtests-Dummy-Host [C3.1 18.210.100.18:9443 in_progress socket-flow (satisfied (Path is satisfied), interface: en5)] event: flow:start_connect @0.003s
default 09:59:21.583422-0600 XCtests-Dummy-Host [C3 test.iwins.kyrio.com:9443 in_progress resolver (satisfied (Path is satisfied), interface: en5)] event: resolver:receive_dns @0.041s
default 09:59:21.598275-0600 runningboardd Invalidating assertion 42771-30114-20288 (target:[application<com.cablelabs.XCtests-Dummy-Host>:30114]) from originator [application<com.cablelabs.XCtests-Dummy-Host>:30114]
default 09:59:21.614116-0600 XCtests-Dummy-Host nw_socket_handle_socket_event [C3.1:3] Socket received CONNECTED event
default 09:59:21.614617-0600 XCtests-Dummy-Host nw_flow_connected [C3.1 18.210.100.18:9443 in_progress socket-flow (satisfied (Path is satisfied), interface: en5)] Transport protocol connected
default 09:59:21.615010-0600 XCtests-Dummy-Host [C3.1 18.210.100.18:9443 in_progress socket-flow (satisfied (Path is satisfied), interface: en5)] event: flow:finish_transport @0.072s
default 09:59:21.615104-0600 XCtests-Dummy-Host [C3 test.iwins.kyrio.com:9443 in_progress resolver (satisfied (Path is satisfied), interface: en5)] event: flow:finish_transport @0.073s
default 09:59:21.615765-0600 XCtests-Dummy-Host boringssl_session_apply_protocol_options_for_transport_block_invoke(1689) [C3.1:2][0x7fd994007720] TLS configured [min_version(0x0303) max_version(0x0304) name(test.iwins.kyrio.com) tickets(false) false_start(false) enforce_ev(false) enforce_ats(false)]
default 09:59:21.615888-0600 XCtests-Dummy-Host boringssl_context_info_handler(1821) [C3.1:2][0x7fd994007720] Client handshake started
default 09:59:21.616031-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C3.1:2][0x7fd994007720] Client handshake state: TLS client enter_early_data
default 09:59:21.616127-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C3.1:2][0x7fd994007720] Client handshake state: TLS client read_server_hello
default 09:59:21.687233-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C3.1:2][0x7fd994007720] Client handshake state: TLS 1.3 client read_hello_retry_request
default 09:59:21.687321-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C3.1:2][0x7fd994007720] Client handshake state: TLS 1.3 client read_server_hello
default 09:59:21.687553-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C3.1:2][0x7fd994007720] Client handshake state: TLS 1.3 client read_encrypted_extensions
default 09:59:21.687627-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C3.1:2][0x7fd994007720] Client handshake state: TLS 1.3 client read_certificate_request
default 09:59:21.687697-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C3.1:2][0x7fd994007720] Client handshake state: TLS 1.3 client read_server_certificate
default 09:59:21.687946-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C3.1:2][0x7fd994007720] Client handshake state: TLS 1.3 client read_server_certificate_verify
default 09:59:21.688132-0600 XCtests-Dummy-Host boringssl_context_evaluate_trust_async(1510) [C3.1:2][0x7fd994007720] Performing external trust evaluation
default 09:59:21.688226-0600 XCtests-Dummy-Host boringssl_context_evaluate_trust_async_external(1495) [C3.1:2][0x7fd994007720] Asyncing for external verify block
default 09:59:21.688361-0600 XCtests-Dummy-Host Connection 3: asked to evaluate TLS Trust
default 09:59:21.688733-0600 XCtests-Dummy-Host TLS Challenge: method to authenticate is: NSURLAuthenticationMethodServerTrust
default 09:59:21.688935-0600 XCtests-Dummy-Host testing server
default 09:59:21.689085-0600 XCtests-Dummy-Host container_create_or_lookup_app_group_path_by_app_group_identifier: success
default 09:59:21.697484-0600 runningboardd Attempting to rename power assertion 33826 for target application<com.cablelabs.XCtests-Dummy-Host> to application<com.cablelabs.XCtests-Dummy-Host>42771-42897-20285:Developer testing(BackgroundUI)
default 09:59:21.697494-0600 runningboardd Calculated state for application<com.cablelabs.XCtests-Dummy-Host>: running-active (role: UserInteractiveNonFocal)
default 09:59:21.699529-0600 XCtests-Dummy-Host Task <C39EAD13-8E45-4227-896A-586B12BA7969>.<1> auth completion disp=0 cred=0x600001c182e0
default 09:59:21.703618-0600 trustd cert[1]: MissingIntermediate =(leaf)[force]> 0
default 09:59:21.704066-0600 XCtests-Dummy-Host Trust evaluate failure: [root MissingIntermediate]
default 09:59:21.704113-0600 XCtests-Dummy-Host System Trust Evaluation yielded status(-9802)


And this is the success case when using the IP in the url. Everything else is the same, code & cert-wise.
Code Block default 09:49:16.792469-0600 XCtests-Dummy-Host Connection 4: asked to evaluate TLS Trust
default 09:49:16.792756-0600 XCtests-Dummy-Host TLS Challenge: method to authenticate is: NSURLAuthenticationMethodServerTrust
default 09:49:16.792927-0600 XCtests-Dummy-Host testing server
default 09:49:16.801076-0600 XCtests-Dummy-Host Connection 4: TLS Trust result 0
default 09:49:16.801112-0600 XCtests-Dummy-Host boringssl_context_evaluate_trust_async_external_block_invoke_3(1451) [C4:2][0x7fac4b00f2f0] Returning from external verify block with result: true
default 09:49:16.801148-0600 XCtests-Dummy-Host boringssl_context_certificate_verify_callback(1609) [C4:2][0x7fac4b00f2f0] Certificate verification result: OK
default 09:49:16.801326-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C4:2][0x7fac4b00f2f0] Client handshake state: TLS 1.3 client read_server_finished
default 09:49:16.801398-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C4:2][0x7fac4b00f2f0] Client handshake state: TLS 1.3 client send_end_of_early_data
default 09:49:16.801429-0600 XCtests-Dummy-Host boringssl_context_info_handler(1836) [C4:2][0x7fac4b00f2f0] Client handshake state: TLS 1.3 client send_client_certificate
default 09:49:16.801503-0600 XCtests-Dummy-Host boringssl_context_certificate_request_callback(1562) [C4:2][0x7fac4b00f2f0] Asyncing for challenge block
default 09:49:16.801589-0600 XCtests-Dummy-Host boringssl_context_certificate_request_callback(1562) [C4:2][0x7fac4b00f2f0] Asyncing for challenge block
default 09:49:16.801708-0600 XCtests-Dummy-Host Connection 4: asked for TLS Client Certificates


Thanks Matt - yeah, this stuff is kinda black magic to me. I saw the empty policy array and didn't understand it, so I thought it just needed to be there. Do I need different policies for using an IP or for using a domain? Are there any good examples of how/why to use policies with the trust?

default 09:59:21.703618-0600 trustd cert[1]: MissingIntermediate =(leaf)[force]> 0
default 09:59:21.704066-0600 XCtests-Dummy-Host Trust evaluate failure: [root MissingIntermediate]
default 09:59:21.704113-0600 XCtests-Dummy-Host System Trust Evaluation yielded status(-9802)

This does seem to indicate that there is a missing intermediate, but it's not clear why you are only receiving this on the hostname side. I'd recommend opening a TSI if you want me to dig into this further.

Do I need different policies for using an IP or for using a domain? Are there any good examples of how/why to use policies with the trust?

I mention the SecPolicyCreateBasicX509 policy here because this checks the x509 policies associated with the certificate to make sure you are not accepting a bad certificate for TLS. I also suggest this because you cannot use a SecPolicyCreateSSL here because there does not appear to be a valid hostname. There are no examples of this that I am aware of. This would be an enhancement request.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Thanks Matt. I have a pared down sample project that demonstrates the condition. I am going to submit a TSI this morning. I will have a private repo on github containing the project. Can you give me a github username to invite for access to the repo?
SSL Error 1200 when using domain name, but IP works fine
 
 
Q