Java would like a word with you :(
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