> There is another aspect, as well. You need to be able to express those issues as errors, otherwise the user is going to be left with a broken box and no idea what is going on.
TLS libraries are terrible at this. Part of it is C: the "integer is the only error type you'll ever need" cannot convey the necessary context, such as which cert is the problem.
Having a library might also help with path building bugs. I've seen bugs in both OpenSSL and GnuTLS in building a correct path, and both just from when the ISRG cross-sign expired.
… and maybe a more complicated data structure in /etc/certs (or whatever the path is) will keep prying vendors out. I've seen a lot of people make out of band changes there, and I would suspect those qualify as "undefined behavior", but OpenSSL's docs don't seem to answer the question of "what happens if vendors do random stuff?" (Like drop non-CA certs into the list of CAs, or don't update the symlinks that seem to form an index…)
Not really; I mean, sure, the validation function will probably rely on returning an integer error code, but there's no reason that the API couldn't also provide a function to retrieve the certificate chain at hand, where an application could walk the chain backwards to the root and figure out the last one (er, or first one, depending on how you look at it) that isn't trusted.
Whether an application would choose to go to the trouble to make use of these APIs is another matter, of course. At any rate, from perspective of the average user, TLS errors are already not so easy to understand, so giving detailed information in this case might not be all that helpful anyway. Just a simple "we don't trust this website, so you shouldn't either" is about as much as most users would understand, probably.
The various TLS libraries already agree on at least one thing: what certificates look like on disk (e.g. PEM and DER formats). There's no reason why some committee couldn't sit down and standardize a format for ancillary data such as the trust date cutoffs mentioned here. Then the TLS libraries would have to implement it, and return appropriate errors from their validation functions. This could possibly be done in a way without needing applications to also be updated, though that depends on how each TLS library reports validation errors.
But even if a particular TLS library doesn't have a way to express these errors properly, I think it's better to have a user confused as to why they can't connect to a site, than silently connect to a site "protected" by a certificate that shouldn't be trusted.
Java would like a word with you :(
Java actually solves the complaint in this thread, and has done for a long time. The JDK has its own root store program run by Sun and now Oracle, TLS APIs use the bundled root store automatically, it provides tools to manage that root store, exceptions are easily rendered to users in a generic way, and it provides a mix of high and low level APIs.
What you're probably thinking of is the JKS format. Java defined a way to represent a root store on disk that's Java specific, I think because there was no standard at the time. But it migrated to the PKCS#12 format in recent releases.
What everyone's realized in recent years is that trust is context-dependent, and that you don't want to use the same roots of trust (or trust anchors, or CAs, though each of those terms has a slightly different meaning) everywhere, nor do you want to co-mingle them. It doesn't make sense to have one file that specifies "Here are all the CAs I trust, and here are rules for each of them"; It makes a lot more sense to have configuration per use-case of "Here are all the CAs for this usecase", and to rely much more on code within the program to disambigutate. Having one file with all those CA certificates and your private keys is... Backwards. Keytool and their custom format at least kept a clear distinction between "these are my private keys" and "these are keys I trust". PKCS#12 has no such distinction, and the tooling around it (Particularly Java's!) makes it hard to disambiguate safebags, so you're left with one giant mess.
It's actually the same problem as Global Variables[1]; Security and trust isn't one object. It's many. One program may act as itself to many providers, but it might also act as three or four different roles within it's scope to the same provider.
In many use cases you could use a store with only a self-signed certificate. That works, the APIs make it straightforward enough. It has many advantages when possible, e.g. you can set the expiry time to so far in the future it never expires, you don't have to muck about with LetsEncrypt and CAs. You do have to control the client and the server. If your frontend is a web app that's a problem. If it's a mobile or desktop app that's easy. Control over the client has other advantages too like not needing HTTP load balancers anymore (in many cases), reducing costs.
But most modern apps don't just connect to the internet, they connect to internal stuff like databases using TLS as well. It's actually a valid strategy to just use the web trust pool for that as well - I personally use it in places, with the certificate managed by CertBot reused for the webserver and the postgres server.
It's not a solution for the modern enterprise, where mutual TLS is sometimes a requirement and is attractive if not. You will want to have at least one non-web CA pool (Trust Bundle) for your enterprise, and sometimes several more for other departments, or other companies you collaborate with.
PKCS12 keystores work fine, however.
* https://openjdk.org/jeps/229
They already have (https://www.rfc-editor.org/rfc/rfc5280.html, https://www.rfc-editor.org/rfc/rfc6960.html) but CRL/OCSP functionality not implemented in most libraries because it's hard and thankless work.
Or at least, I'm certainly not aware of any mechanism in the CRL standard to "partially revoke" a CA certificate based on complex conditions like "What's the notBefore date on the leaf certificate we're currently validating?".
The problem is, code that handles these things is safety critical and requires real and sustained investment to be reliable. Given this overhead (and the gaps in CRL/OCSP support that I mentioned), it's unclear if this use case will be common enough to commit to supporting.
We might need something more - but I wonder i trust-stores could be modified so that:
- every device get a "host" CA (like a ssh key)
- this CA is the only one trusted
- in turn, this CA signs issuer/top-level CA (or even cross-signs the intermediary certs directly) (eg with - new/-force_pubkey)
Additional logic could be applied in the signing step - say rules to set the validity, or rules to sign/not-sign. One might set a 48h lifetime, and run a crown job every night - allowing for "dynamic" "revocation" (cert expiry).
Not sure if this would work out of the box though - I have not looked that deep into ca and ca trust.
The reason the Linux root stores don't have a notion of 'trust until' like the article wants is simple - nothing stops an untrusted CA just issuing certificates that claim to have been issued before the cutoff, so it seems pointless. Browsers have ways to tackle that like Certificate Transparency or crawling the web to try and identify every extant cert, but most apps don't use that.
However, now CAs can't really do this anymore, because - as you say - they'd risk immediate exclusion from browsers if this is detected via CT analysis. So Linux distros can actually benefit from CT and browser's impact in the CA space in an indirect manner.
There is definitely a power imbalance though. E.g., even if a distro implemented "trust until", they could not realistically make their own rules that are stricter than what browsers do: CAs could backdate certs so they get accepted by the distro, but if browsers consider the CA fully trusted, they might not care about the backdated certificates.
(I'm the author of the linked-to entry.)
I started to think (but not check specs): Is it even defined what domain name should be checked? MX records can be different from server names. Maybe certificate checking is just not defined for SMTP over TLS?
The decision of whether or not to believe the authenticity of the
other party in a TLS negotiation is a local matter. However, some
general rules for the decisions are:
- A SMTP client would probably only want to authenticate an SMTP
server whose server certificate has a domain name that is the
domain name that the client thought it was connecting to.
However in the case of Postfix there’s also the matter of connection to services like an ldap server to check things. In that case it’ll also by default happily connect to an ldaps service with a self signed, expired certificate.P.S. As you probably guessed in my previous comment "3 months later" should of course have been "3 months earlier". Sorry about the mistake.
I suppose you could write up a way to specify this in DNS but it’d take decades to be implemented and you would have to deal with pushback from the snoops who I’m sure don’t mind they can peep into the email you receive.
(b) is a pretty tough problem!
But that just tells us where the root certs are, not much more. In the API, I load the root CAs and check validity of the cert, nothing beyond that.
And I can do that in multiple different TLS engines.
Given that there is no API for this, all the TLS engines need to re-implement this logic. Hell, in most cases, even things like validity range checks are handled by the applicative logic.
On Windows, there is the builtin TLS API `schannel` which means that you have a lot more structure / order in this. Including the ability to express those sort of details.
There is another aspect, as well. You need to be able to express those issues as errors, otherwise the user is going to be left with a broken box and no idea what is going on.