Certificate is not standards compliant on MacOS/iOS, error -9802, strict TLS Trust evaluation failed

This is a little rant about Apple and how they implemented their security checks around certificates. TL;DR: You can’t have a certificate that is valid for more than 825 days.

Like many others, I have a private trusted CA certificate installed on my MacOS. It’s in the keychain an marked as trusted. So far everything worked well, but one day I got error messages when connecting to a Jabber server (XMPP with Starttls). I got it for all software that used the MacOS TLS stack, but not for software like Firefox. So it had to be something Apple specific. The CA certificate was shown as trusted, the intermediate certificate was accepted, but the leaf certificate (certificate of the server) had an error tagged on to it:

certificate is not standards compliant

Wow, what a helpful error message! Now I definitely know what to do. So I started investigating what the problem could be. The first few hits you get when searching the web are totally red herrings, but I didn’t know at that time: crypto/x509: “certificate is not standards compliant” on MacOS and SSL certificate is not Standards compliant“. Of course after reading that I thought I knew what the problem was: The new certificate transparency rules.

Red herring: Certificate transparency

My CA doesn’t have a certificate transparency log, that would make sense I thought at first… but then read about how you have to apply at Apple to get on the list, which obviously isn’t what other people do as there were only official CAs on the list. Searching a little further you find some posts that say internal/private CAs are excluded from certificate transparency in Chrome. That makes sense, but what about Apple? Couldn’t find any information there. So just to debug this, let’s just try to get rid of Certificate Transparency on my local MacOS and see if that helps. How do you do that? Well, only via device management profiles (aka .mobileconfig file). I knew those from my MDM analysis days, so I fired up Apple Configurator to create a new profile. In the profile I was really able to set domains (wildcard-style) that should be excluded from certificate transparency, great! So I exported the profile to a .mobileconfig file and tried to install it. However, when trying to install it I got the following error:

Profile installation failed. A Certificate Transparency Settings payload can only be included in a device profile.

Try searching for that error message on the web. There wasn’t a single hit at the time of writing… So what’s a device profile? I vaguely remembered, but I had to look it up first. The documentation of Apple on how to install profiles didn’t help either. Then I thought maybe I need to sign the .mobileconfig to make it install as a device profile. So I took a random cert/private key from a trusted CA I had (yes, you can take any) and signed the .mobileconfig. It worked and it’s weird, but when you sign with any certificate, the profile is actually shown as “verified”. However, that didn’t help either, couldn’t install as device profile and I couldn’t remember how I used to do it. I searched for a while but couldn’t find a solution.

Unhelpful error messages

Ok, I was stuck there, it would take even more time to figure out how to install that device profile. Let’s go back to square one. So I checked the console for Safari (which only says “can’t established secure connection”) specific errors regarding the certificate “not being standards compliant”. I checked Wireshark (which was pointless, I know, I should only see an “encrypted alert” packet, but I was desperate). I copy pasted some swift code I could run in the “swift repl” interactive console to get an error message:

import Foundation

let url = URL(string: "https://foo.example.org/")!
let (data, _) = try await URLSession.shared.data(from: url)

But the only unhelpful messages I got was:

2024-01-30 18:02:02.103330+0100 repl_swift[29733:501906] Connection 1: strict TLS Trust evaluation failed(-9802)
2024-01-30 18:02:02.103370+0100 repl_swift[29733:501906] Connection 1: TLS Trust encountered error 3:-9802
2024-01-30 18:02:02.103376+0100 repl_swift[29733:501906] Connection 1: encountered error(3:-9802)
2024-01-30 18:02:02.206271+0100 repl_swift[29733:501906] Connection 2: strict TLS Trust evaluation failed(-9802)
2024-01-30 18:02:02.206310+0100 repl_swift[29733:501906] Connection 2: TLS Trust encountered error 3:-9802
2024-01-30 18:02:02.206319+0100 repl_swift[29733:501906] Connection 2: encountered error(3:-9802)
2024-01-30 18:02:02.259575+0100 repl_swift[29733:501906] [boringssl] boringssl_context_handle_fatal_alert(1991) [C3.1.1.1:2][0x100307470] read alert, level: fatal, description: protocol version
2024-01-30 18:02:02.263724+0100 repl_swift[29733:501906] [boringssl] boringssl_session_handshake_incomplete(88) [C3.1.1.1:2][0x100307470] SSL library error
2024-01-30 18:02:02.263782+0100 repl_swift[29733:501906] [boringssl] boringssl_session_handshake_error_print(43) [C3.1.1.1:2][0x100307470] Error: 4328543672:error:1000042e:SSL routines:OPENSSL_internal:TLSV1_ALERT_PROTOCOL_VERSION:/AppleInternal/Library/BuildRoots/9ba9e62a-acc5-11ee-9f46-d64f9dd5e0b3/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/tls_record.cc:594:SSL alert number 70
2024-01-30 18:02:02.263797+0100 repl_swift[29733:501906] [boringssl] nw_protocol_boringssl_handshake_negotiate_proceed(774) [C3.1.1.1:2][0x100307470] handshake failed at state 12288: not completed
2024-01-30 18:02:02.264613+0100 repl_swift[29733:501906] Connection 3: received failure notification
2024-01-30 18:02:02.264780+0100 repl_swift[29733:501906] Connection 3: failed to connect 3:-9836, reason -1
2024-01-30 18:02:02.264808+0100 repl_swift[29733:501906] Connection 3: encountered error(3:-9836)
2024-01-30 18:02:02.265218+0100 repl_swift[29733:501906] Task <9E99888A-E626-467B-A532-FDC6E7BC7F90>.<1> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9836])
2024-01-30 18:02:02.269813+0100 repl_swift[29733:501905] Task <9E99888A-E626-467B-A532-FDC6E7BC7F90>.<1> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSErrorFailingURLStringKey=https://foo.example.org/, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <9E99888A-E626-467B-A532-FDC6E7BC7F90>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <9E99888A-E626-467B-A532-FDC6E7BC7F90>.<1>"
), NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://foo.example.org/, NSUnderlyingError=0x600000c18600 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9836, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9836, _NSURLErrorNWPathKey=satisfied (Path is satisfied), viable, interface: utun3, dns}}, _kCFStreamErrorCodeKey=-9836}

Please be aware that only the first three lines are relevant for our problem (TLS1.3 connection failing because “strict TLS Trust evaluation failed”) and that the error message is not helpful at all. A different error message indeed, but searching for that on the web didn’t help either. Everything after the first 3 lines is just boringssl having issues connecting with TLS1.0 etc. So this means there is no error that would help me figure it out, again. Thanks Apple.


So I asked on Mastodon and someone pointed out the following link: Requirements for trusted certificates in iOS 13 and macOS 10.15. Which had the solution: TLS server certificates must have a validity period of 825 days or fewer (as expressed in the NotBefore and NotAfter fields of the certificate). As soon as we changed to shorter certificate validity, the errors were gone.

All in all I would say this is a pretty bad example for Apple. Even though they say on their Technical Note TN2232 – HTTPS Server Trust Evaluation that you should never disable TLS verification. The openssl examples in there also don’t help in this case. I can see why developers would give up if they don’t get proper error messages with reasons included. Or error codes that can be looked up. Or documentation that easily tells you howto debug it and get details. Or just one single proper documentation what Apple’s view on “standards compliant certificate” could be.

Bad practice, dark pattern. There, I wrote it.

Android Nougat’s certificate pinning security mechanism

If you are a pentester like me, chances are you are doing mobile application reviews on Android. One of the most important things to check is the server API. On the other hand we might want to see what possibilities a server has to influence the Android app with its responses. For both the easiest and most straight forward method is to do a Man-In-The-Middle attack in the lab and look at the network traffic. How do we do this if the mobile app uses TLS? Easy, just install a user CA certificate.

Before Android 7 that was straight forward. After you installed the CA certificate, there was a little annoying screen showing a warning in the notifications every time you start up your phone, but it worked fine for everyone. However, starting with Android 7 installing a CA certificate is not affecting mobile apps, I tested that and the official announcement about this user-added certificate security is here. User installed CA certificates won’t be trusted by mobile apps and Android claims there is some security gain from this. So let’s look at this new “security” feature of Google’s Android.

First of all who is affected by this security feature? I think only the defender side has to jump through this hoop. Every real-world attack vector I can think of is not very realistic. First of all, a user would need to fully cooperate to let an attacker exploit this. As Android is not opening the security settings automatically when you download a certificate (like iOS), an attacker would have to convince the user to go to the settings dialogue, go to the security settings, scroll down, tap on “install certificate” and choose the correct file from the file system. Let’s say an attacker will setup a Wi-Fi access point and forces the user to do this or otherwise the user won’t get Internet access. This is the only scenario I can think of where a user might at all consider installing such a certificate. You might say that can happen with non-technical users, but then why don’t we just add a big red warning that websites trying to convince you to install a CA certificate are evil? That would suffice in my opinion. If a user would be so ignorant and install an unknown CA despite the warnings, we are in much bigger trouble. That user will probably also type all his usernames and passwords into any website forms that look remotely like a known login form, has an invalid TLS certificate or doesn’t use TLS at all. So for example the attacker could do easy phishing. For users of personal Android phones this is probably the biggest issue.

But let’s also consider corporate Android phones. I understand that administrators don’t want their users to decide on such a security critical topic. But why doesn’t Android just implement an Administrator API rule that would disabling installation of user CA certificates and delete all already installed ones on managed phones? There is already an Administration API that does various such things.

Secondly, why does Android think that a user installed certificate is less trusted than the hundreds of preinstalled, nation-state-attacker-owned CAs? Your Android already comes with various preinstalled CAs, which are not very thrustworthy in my opinion.

It seems Android is raising the bar for defenders, not for attackers. I don’t believe Android is defending against any real attack vector. It boarders to pretending to do security (snakeoil).

On the other hand I know how to disassemble an app and reassemble it to circumvent this new security feature. There are also Android apps that will allow you to install CA certificates in the root CA store on rooted phones, which is by far the best solution on rooted phones. Use Magisk and you have your perfect phone for security analysis.

I thought I’ve seen many strange Android security decisions, this is exceptional. Or is it just me?

Free OWASP membership


  • Beginning of 10.2011: OWASP was informed (including details) that the OWASP membership registration has a logic flaw (“please inform vendor”).
  • Beginning of 10.2011: Response from OWASP, vendor can not reproduce problem. Sent more details.
  • Beginning of 10.2011: Response from OWASP, vendor still can’t reproduce problem. Sent video below.
  • 19.10.2011: Bug report opened.
  • 15.02.2012: Checked back and asked OWASP if problem is resolved.
  • 26.02.2012: They don’t know. Checked flaw again, it still exists. Advised OWASP to get in touch with one of the organisation’s security expert to handle the issue (no response from OWASP).
  • 30.03.2012: Checked flaw again, it still exists. Informed OWASP and vendor directly that the video will be released in two weeks if it doesn’t get fixed.
  • 30.03.2012: Response from OWASP, they would find a solution until end of April. Agreed to wait until end of April.
  • 04.04.2012: Response from vendor, it’s fixed.

In my opinion half a year is long enough. Putting on some more pressure (regarding the release of the video) worked very well. I felt like I owe it to all the paying OWASP members.

Enough words, enjoy the video: https://www.floyd.ch/download/free-owasp-membership.mov