SWIFT: server certificate does NOT include an ID which matches the server name

I'm working on MBP OSX Ventura 13.5.2 I'm working with Swift 5 (Xcode 15.2) I have a local httpd configured with vhosts. I create my local certs using mkcert.

When I visit the https://example site with Chrome the certificate is perfectly valid and there are no issues.

When I try and contact the same site using a DataCallBack function to the URL I get the error "server certificate does NOT include an ID which matches the server name"

In the log:

Connection 1: default TLS Trust evaluation failed(-9807)
Connection 1: TLS Trust encountered error 3:-9807
Connection 1: encountered error(3:-9807)

Answered by DTS Engineer in 802047022
So I dragged my CA root certificate (JacarandaCA.p12) to the simulator

That’s not right. You don’t want to install the digital identity, but rather just the certificate.

I ran through these steps today, just to be sure, and things are still working for me. Here’s what I did:

  1. Using Xcode 16.0b6 on macOS 14.6.1, targeting the iOS 18.0b7 simulator…

  2. Create a new app project from the iOS > App template.

  3. Add a Test button and wire it up to the test function; the code for that is at the end of this post.

  4. Build and run it on the simulator.

  5. In the simulator, tap the app’s Test button. It logs:

    will run task
    did not run task, error: NSURLErrorDomain / -1200
    

    As expected, the app can’t access https://www.cacert.org because its certificate was not issued by a trusted root.

  6. Stop the app.

  7. On the Mac, go to the CAcert website http://www.cacert.org/index.php?id=3 and download Root Certificate (DER Format). That yields a file called root_X0F.der.

  8. Drag that into the simulator.

  9. In the simulator, there’s an alert saying “This website is trying to download a configuration profile. Do you want to allow this?” Tap Allow.

  10. There’s a second alert saying “Profile Downloaded; Review the profile in the Settings app if you want to install it.” Tap Close.

  11. Still in the simulator, launch Settings.

  12. There’s now a Profile Downloaded entry. Tap it.

  13. Run through the install process.

  14. Once you’re done, navigate to Settings > About > Certificate Trust Settings and enable CA Cert Signing Authority.

  15. Back in Xcode, run the app again.

  16. And in the app, tap the Test button again. It prints:

    will run task
    did run task, status: 200, bytes: 15280
    

    The app is now able to access https://www.cacert.org because the CAcert root is installed in the simulator.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


func test() async {
    do {
        print("will run task")
        let url = URL(string: "https://www.cacert.org")!
        let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 60.0)
        let (data, response) = try await URLSession.shared.data(for: request)
        let httpResponse = response as! HTTPURLResponse
        print("did run task, status: \(httpResponse.statusCode), bytes: \(data.count)")
    } catch let error as NSError {
        print("did not run task, error: \(error.domain) / \(error.code)")
    }
}

I should also mention, the url domain name I use from Chrome is the same url domain name I use from Swift.

Error -9807 is errSSLXCertChainInvalid, which means that the system wasn’t able to build a certificate chain from the leaf to a trusted root. How you handle that depends on the API you’re using and eventual deployment plan.

Let’s start with APIs. You wrote:

When I try and contact the same site using a DataCallBack function

That’s not an Apple API that I recognise. Are you using a third-party framework here? Or is this your code and it’s calling some Apple API, like URLSession?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn

Thanks for your assistance, I appreciate it.

The "DataCallBack" is my function is used to send a request to a web server and handle the response.

        let URL = NSURL(string: serverUrl)!
        
        var urlRequest = URLRequest(url: URL as URL)
        urlRequest.httpMethod = "POST"
        urlRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        urlRequest.setValue("keep-alive", forHTTPHeaderField: "Connection")
        urlRequest.httpBody = formData.data(using: String.Encoding.utf8)

        // This is the call to the server - response is handled by a closure
        let task = Singleton.shared.getUrlSession().dataTask(with: urlRequest) { (data, response, error) in .... }

Do you need more code???

There were more messages in the response, but I'm having trouble identifying what the base issue is.

Task <2844EFE1-427E-4239-A00F-6538186A598E>.<1> finished with error [-1202] Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “xpedite” which could put your confidential information at risk." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
    "<cert(0x10582ac00) s: murraycollingwood@192-168-1-20.tpgi.com.au (Murray Collingwood) i: mkcert murraycollingwood@kohekohe.local (Murray Collingwood)>"
), NSErrorClientCertificateStateKey=0, NSErrorFailingURLKey=https://xpedite/rx/rx.php, NSErrorFailingURLStringKey=https://xpedite/rx/rx.php, NSUnderlyingError=0x600000c64060 {Error Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x6000033015e0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9807, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9807, kCFStreamPropertySSLPeerCertificates=(
    "<cert(0x10582ac00) s: murraycollingwood@192-168-1-20.tpgi.com.au (Murray Collingwood) i: mkcert murraycollingwood@kohekohe.local (Murray Collingwood)>"
)}}, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <2844EFE1-427E-4239-A00F-6538186A598E>.<1>"
), _kCFStreamErrorCodeKey=-9807, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <2844EFE1-427E-4239-A00F-6538186A598E>.<1>, NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x6000033015e0>, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “xpedite” which could put your confidential information at risk.}

Is it just that there is no response or that the response isn't a in a JSON format? I think that's just a red herring.

Cheers mc

Thanks for confirming that you’re using URLSession.

Is it just that there is no response or that the response isn't a in a JSON format?

Neither. The -1202 error is NSURLErrorServerCertificateUntrusted, which indicates that URLSession was unable to start a TLS session to run the request over.

The best path forward here depends on the final disposition of your server. Will your app eventually talk to a server that’s available on the public Internet? And will have a certificate issued by a standard CA?

Or are you targeting some other environment?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

This is a development certificate only.
The final production version will access using a real certificate. So ideally I just want a method that will work for my development environment please?

Cheers Murray

Hello @DTS Engineer Are you still there?

Sorry, I missed your earlier reply (r. 131905835).

The final production version will access using a real certificate.

OK, cool. In that case I recommend that you follow the advice in QA1948 HTTPS and Test Servers. That prevents you from having to tweak your code to disable server trust evaluation, which is a common cause of horrendous security vulnerabilities.

You can create a CA and use it to issue certificates on the Mac; see Technote 2326 Creating Certificates for TLS Testing for the details. However, that’s not a requirement. If you have a third-party tool to run your CA, that’s cool too.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn

If it were only that easy!

  1. I used mkcert to create a CAROOT certificate and a certificate for my web server. The CAROOT is installed in the key chain, and trusted.

The certificate for the web server (https://xpedite) is installed in vhosts

        ServerName xpedite
        ServerAdmin murray@focus-computing.com.au
        DocumentRoot "/Users/murraycollingwood/dev/xpedite/xpedite-website/build"
        
        DirectoryIndex index.php ui/home.php home.php
        <Directory />
                Options -Indexes
                Require all granted
        </Directory>

        ErrorLog "/Users/murraycollingwood/dev/xpedite/xpedite-website/error.log"
        CustomLog "/Users/murraycollingwood/dev/xpedite/xpedite-website/access.log" combined

	SSLEngine on
	SSLCertificateFile /Users/murraycollingwood/local-cert/xpedite.pem
	SSLCertificateKeyFile /Users/murraycollingwood/local-cert/xpedite-key.pem

	SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
        SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        SSLHonorCipherOrder off

</VirtualHost>
  1. From Chrome when I visit the website it looks fine.

  1. Even after all this I'm still getting the same errors.
Connection 1: default TLS Trust evaluation failed(-9807)
Connection 1: TLS Trust encountered error 3:-9807
Connection 1: encountered error(3:-9807)
Task <372F75AC-F140-426B-B324-F566412E9712>.<1> HTTP load failed, 0/0 bytes (error code: -1202 [3:-9807])
Task <372F75AC-F140-426B-B324-F566412E9712>.<1> finished with error [-1202] Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “xpedite” which could put your confidential information at risk." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
    "<cert(0x10404c200) s: murraycollingwood@192-168-1-20.tpgi.com.au (Murray Collingwood) i: mkcert murraycollingwood@kohekohe.local (Murray Collingwood)>"
), NSErrorClientCertificateStateKey=0, NSErrorFailingURLKey=https://xpedite/rx/rx.php, NSErrorFailingURLStringKey=https://xpedite/rx/rx.php, NSUnderlyingError=0x600000c60330 {Error Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x600003305180>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9807, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9807, kCFStreamPropertySSLPeerCertificates=(
    "<cert(0x10404c200) s: murraycollingwood@192-168-1-20.tpgi.com.au (Murray Collingwood) i: mkcert murraycollingwood@kohekohe.local (Murray Collingwood)>"
)}}, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <372F75AC-F140-426B-B324-F566412E9712>.<1>"
), _kCFStreamErrorCodeKey=-9807, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <372F75AC-F140-426B-B324-F566412E9712>.<1>, NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x600003305180>, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “xpedite” which could put your confidential information at risk.}
  1. I have the name 'xpedite' in my /etc/hosts:
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1       localhost jacaranda sobs flat isolink xpedite
255.255.255.255 broadcasthost
::1             localhost xpedite

I feel like I'm missing a step here, but I'm not sure where it is.

Hoping you can help.

Cheers Murray

Hmmm. I’m not sure why this is failing. I use this in two scenarios:

  • For my personal web server which is on the public Internet and has a standard DNS name [1].

  • For internal testing, using the server’s .local DNS name, for example, fluffy.local [2].

There are two obvious differences between your setup and those scenarios:

  • You’re not using DNS, but rather messing around with the hosts file.

  • I used Certificate Assistant to issue my certs.

I think the first point is the easiest for you to explore. If you switch to using your server’s .local name, does that help?

Also, one (seemingly :-) random question: Are you running the URLSession code in an app? Or in a non-app context, like a command-line tool?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] One day I’ll get around to setting up Let’s Encrypt, but today is not that day.

[2] Technically this should be fluffy.local. but that trailing dot causes problems for HTTPS server trust evaluation.

Hi Quinn

mkcert = 3 steps to create a certificate
Certificate Assistant = 137 steps to create a certificate

I have used the Certificate Assistant before (like 10 years ago), and it took a while to step through but it did work. Obviously the mkcert was very attractive as it is so much quicker.

I will try the Certificate Assistant and hopefully get a better result.

Cheers Murray

[made a mistake - but can't remove the reply]

Certificate Assistant = 137 steps to create a certificate

Yep. That’s why I’m trying to avoid sending you down that path by recommending that you test with a .local DNS name first.

Oh, but the comparison is a little unfair because of this:

% mkcert
zsh: command not found: mkcert

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn Sorry for the break - I've been on holiday.
So I went through all 137 steps using the Certificate Assistant to create the Root CA and then the SSL server certificate for my domain (xpedite.local). I was very careful to follow the instructions to the letter.

I converted both certificates to Base64, appended the RootCA to the xpedite.crt and added the crt and key to my vhosts file.

Browsing to the website didn't work. I then used the KeyChain to "Always trust" the Root CA (this didn't happen by default, and the images from the article showed the same issue all the way through.

Browsing to the website now works.

Using my Swift app (in the simulator) still fails with the same error (below).

Connection 1: default TLS Trust evaluation failed(-9813)
Connection 1: TLS Trust encountered error 3:-9813
Connection 1: encountered error(3:-9813)
Task <01D12F73-55BB-4C12-8FD6-3510D284C9FF>.<1> HTTP load failed, 0/0 bytes (error code: -1202 [3:-9813])
Task <01D12F73-55BB-4C12-8FD6-3510D284C9FF>.<1> finished with error [-1202] Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “xpedite.local” which could put your confidential information at risk." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
    "<cert(0x102832400) s: Xpedite Software i: JacarandaCA2>",
    "<cert(0x102832c00) s: JacarandaCA2 i: JacarandaCA2>"

I haven't really made any progress.

Hoping you can help me get over this very annoying hurdle.

Cheers Murray

@DTS Engineer are you still there, please?

@DTS Engineer Hi Quinn

Wow - it's been 4 weeks since I started this thread and I'm still struggling.

Previously I was getting error -9813 - couldn't find the Root CA. So I decided to move it from the "Login" area to the "System" area based on this article: https://blog.arrogantrabbit.com/ssl/Root-CA-macOS/

I deleted all of the Root CAs and certificates previously created. I then followed these instructions to create them again. This time the Root CA was in the "System" area, while the SSL server certificate was left in the "Login" area.

With the certificates in place and the web server restarted, I checked it first with Safari, and the certificates worked fine.

Clearly we can see the SSL server certificate (xpedite.local) issued by the Root CA (Jacaranda). The Root CA is "Always trusted".

When I run my swift mobile app in the simulator however, I'm getting this error:

Connection 1: default TLS Trust evaluation failed(-9807)
Connection 1: TLS Trust encountered error 3:-9807
Connection 1: encountered error(3:-9807)

-9807 indicates "errSSLXCertChainInvalid"

I can't see any error with the Chain. Surely the chain is the link from the certificate to the issuer - but there doesn't appear to be any issue with this. Should I create the SSL server certificate in the "System" area also?

Can you suggest anything else I can try to get this working please? It really is holding up all my work on this project.

Cheers Murray

SWIFT: server certificate does NOT include an ID which matches the server name
 
 
Q