Is it a bad practice to use Java standard keystore - java

We had been using java standard keystore ($JAVA_HOME/jre/lib/security/cacerts) as the trusted store for tomcat. And that tomcat server would communicate with some other server. A recent OS(AIX) upgrade apparently over-wrote the file at $JAVA_HOME/jre/lib/security/cacerts and that resulted in lost certificates and lot of issues with application hosted in tomcat.
Looking at this is it a bad practice to relay up on $JAVA_HOME/jre/lib/security/cacerts ?
What are the alternate (better|standard) ways to tackle this scenario?

It's not a bad practice if you have a build process that will repeat the imports.

Not sure, but assuming your assumptions are correct, caution where you put your keystore. I would strongly suggest it is placed inside Apache folder.
By default in Websphere the keystore works this way, since it brings it's own JVM :)

In terms of what is in the cacerts file, it's not necessarily worse practice than relying on the default CA certificates installed in your OS or your browser, but that doesn't mean it's great.
Sun/Oracle have a little "important note" somewhere in the middle of the JSSE Reference Guide about this:
IMPORTANT NOTE: The JDK ships with a limited number of trusted root
certificates in the /lib/security/cacerts file. As
documented in keytool, it is your responsibility to maintain (that is,
add/remove) the certificates contained in this file if you use this
file as a truststore.
Depending on the certificate configuration of the servers you contact,
you may need to add additional root certificate(s). Obtain the needed
specific root certificate(s) from the appropriate vendor.
In terms of configuration, for specific applications where I've had to install "local" CA certificates, I find it more stable to use a local trust store (for example, specified with javax.net.ssl.trustStore).

Yes it is a bad practice to do that.
The best practice is to have to limit your trusted certificates as much as needed.
So you should have used you own keystore with only the certificates trusted by your application.

The AIX upgrade is a patch. Any patch must not delete / overwrite user data. I would suggest that users affected by this kind of data loss ask IBM to fix the patch procedure. In comparison, a patch of the httpd server does not overwrite / delete the configuration even though it is in the program directory.

Related

Should I use either cacerts or local trustore but not both?

We have a local truststore.jks we use for our app. We pass this to the app using VM Options like so:
-Djavax.net.ssl.trustStore=<<PATH TO TRUSTSOTRE.JKS>>
If we are already using this truststore.jks is there any good reason to import certificates into the cacerts file of our jdk?
Couldn't we just import these certs into out existing truststore.jks?
There is no right or wrong here, and the decision depend on your application the operation and approach to managing security setup.
Personally I would always prefer to use a custom trust store (as you are already doing), only containing the trust path for the endpoints that are required for your app.
This way you would only have to maintain trust for the endpoint(s) you are using.
However, if the app connects to many TLS endpoints using certificates from different CA providers, a trust store based on cacerts could make maintenance easier if/when endpoints changes issuer.
In that case I would advise to not change the cacerts file in the JRE, but base a custom trust store file on the cacerts file for use by the application.

How to make my server trust with a certificate?

On my java application, that runs on Tomcat in a RedHat linux machine, I need to send a request to an API that uses ssl trust certificate. Our partners have already sent us two files that should be used to trust them: wse-tst_partner_com.crt and DigiCertCA.crt.
Our partners have sent these instructions: https://www.digicert.com/csr-ssl-installation/apache-openssl.htm#ssl_certificate_install
However, I'm stuck on step 2, since I didn't find an httpd.conf file, neither apache2.conf file. I wonder if these are not applicable to Tomcat (they didn't actually know what we have before creating the API and their security stuff).
I've also tried to look for installing trust certificates for Tomcat, but the instructions I've found were only to create a new certificate with keytool - I don't want to create a new certificate, I need to "trust" the ones our partners already created.
I've also found this question on serverfault. However, I don't have any .pem files, and I don't want to create new files - I just want to use the ones I mentioned on the beginning of this post.
I'm a noob in this subject - I don't think this should be a complicated task... Should I add something to my server.xml file? Or, maybe I'm looking for the conf files in the wrong place?
httpd.conf and apache2.conf are apache files not tomcat.
They assumed you were using apache instead of tomcat.
Either put an apache in front of tomcat and use that guide or create a keystore with keytool (A keystore is "like a box" containing the certificates) and add the certificates you were provided inside, you will need to add the intermediate and the root as well. And then configure tomcat (listeners, connectors) to use that keystore.
Follow the steps from this guide instead https://www.digicert.com/csr-ssl-installation/tomcat-keytool.htm but ignore the creation of certificate as the certificates are already provided. Make sure you have the key as well, otherwise you'll have to create the CSR in the server and provide that to whoever signs the certificates (your partner I guess) and they will get back to you with new certificates. Incorporate the certs to the keystore as suggested.

Use and resolve URL as -Djavax.net.ssl.trustStore=<url> in JAVA_OPTS PCF

I am working on deploying an application that connects with a third party application via SSL connection. For that earlier I was using these certs and keeping them in the build pack and pick them from there. Now due to limited number of production release I need to externalize the SSL certs. So far I have externalized the certs which were picked from jre/lib/security/wasadm. via a SSLConnectionFactory.
But there are some certs which are part of cacert and needs to be picked from the JAVA_OPTS on PCF as environment variable like -Djavax.net.ssl.trustStore=<URL to a Path on Server>
How can I use -D or JAVA_OPTS in order to retrieve the trustStore from there.
Jdk -> 1.7xx
Target Environment -> PCF
environment OS -> Linux
Would admire any help in here.
keeping them in the build pack and pick them from there.
If at all possible, don't do this. It requires you to fork the buildpack and that is bad. It creates a maintenance burden for you and will slow down your ability to deploy updates and security patches to your apps running on Cloud Foundry.
But there are some certs which are part of cacert and needs to be picked from the JAVA_OPTS on PCF as environment variable like -Djavax.net.ssl.trustStore=
I'm not sure why you'd be forced to go this route. The -Djavax.net.ssl.trustStore option is just setting the default trust store for the JVM. It can be convenient sometimes, but has it's drawbacks.
When you need to trust certs that are not signed by a well-known CA, I would suggest that you make your own application specific trust store, rather than to add more certs to the JVM's default trust store.
Your application will be more portable if it does not depend on system specific customizations like modifying the default trust store. Changing the default trust store is also problematic, because everything running in your JVM uses it. This is generally OK if you're just adding to the default trust store, but if you are trying to remove from it you might end up breaking other code. Plus, if you're using your own trust store you can include only the minimum number of certificates which are required by your app or even by a specific service in your app (since you can use multiple trust stores this way).
I don't know what HTTP client you're using, but there's a good example of doing this with HTTPClient here. See the Custom SSL context example.
Putting that aside, if you really want to adjust the default trust store of the JVM running your app on Cloud Foundry, you just need to modify the JAVA_OPTS environment variable for your app & restage.
Ex:
cf set-env my-cool-app JAVA_OPTS '-Djavax.net.ssl.trustStore=path/to/my/new/default-truststore.jks'
I believe you can use a relative path to your trust store, like if you're bundling it with your application. If not, /home/vcap/app is the path to the root of your JAR/WAR file, so you could insert that if you need a full path. It needs to be a local path on the container though, I don't believe the JVM supports remote paths/URLs.
Jdk -> 1.7xx
You really need to upgrade :)
I have resolved the issue by picking the file from the URL and putting that in my war at startup and hence it solved my all issues. Thanks!

Consequences of storing TrustedCertEntry and PrivateKeyEntry in JKS?

I recently joined a project that has an application running in Tomcat that uses a single file as both the KeyStore and the TrustStore. In other words, it includes both entries of types trustedCertEntry and PrivateKeyEntry.
While upgrading from Tomcat 8.5.6 to 8.5.20, I realized catalina.out was giving me
java.lang.IllegalArgumentException: java.security.KeyStoreException: Cannot store non-PrivateKeys
The solution was to make to remove the trustedCertEntry entries from the keystore.
To me, this seems fairly obvious that you would want to keep these separate. My question is, are there any possible security consequences to using the same file as a keystore and truststore? If so, why does Java (or SSL) allow these to be kept in the same file?
SSL and TLS are interoperable protocols; by IETF policy and tradition they say nothing about storage of anything and everything at either or any endpoint. "That's a local matter."
Java historically used one file format (JKS) for both TrustedCert's and PrivateKey's, not only for SSL/TLS but for all public-key crypto (and optionally with JCEKS some symmetric crypto also), and Java9 is switching to PKCS12 for both. Using the same format doesn't mean you must use the same file, and I would say it's preferable to use separate files, but I don't see an actual security problem in using a single file as long as you keep any file containing a privatekey restricted to one system, or as few systems as absolutely necessary, plus appropriate backup; however that's not really a programming Q and you might try for better answers on security.SX.
Tomcat 8.5 sorta-kinda combines the previously separate and (often confusingly) different config for Java-JSSE and APR=OpenSSL stacks, and I believe this restriction that the keystore can only contain PrivateKey's is a result of that change.

A straight forward way to open self-signed HTTPS SSL URLs in java?

I'm building an application that needs to open self-signed HTTPS SSL URLs in java. I've learned that you can bypass the SSL problems by adding a call to HttpsURLConnection.setDefaultHostnameVerifier() where you say what hostnames are allowed.
However, I have a second problem where my servers are running self-signed certs. So even with the hostname bypass I'm getting exceptions like:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
I've looked around and the only solutions I've found is to add the certificate to the java key store, but this is a problem for me because I have no control over when or how they update java, and I've read that the keystore isn't preserved between updates, and I have no access to the system outside of the JVM.
My application will only make calls to a single server so if there was a way to bypass the self-signed restrictions, and never consult keystores, it wouldn't be a security problem, but is there a way to do this?
I'm building an application that needs to open self-signed HTTPS SSL
URLs in java. I've learned that you can bypass the SSL problems by
adding a call to HttpsURLConnection.setDefaultHostnameVerifier() where
you say what hostnames are allowed.
There are some misconceptions from your question:
Hostname verification is unrelated to whether the certificate is self-signed or not.
It is a verification that matches the domain you are trying to access with the certificate info (CN or Subject Alt Name).
You would need to configure the verifier if you needed to accept a certificate that did not match the url (not recommended at all!!!)
Concerning the self-signed.
This is irrelevant.
You can configure your application to load your custom truststore which would include the certificate your application will trust. In fact this is the best approach (than using Java's cacerts).
All you have to do is import the certificate in a keystore (JKS or PKCS12) and load it in your custom TrustManagers in your application.
Just google arround, plenty of examples E.g. self-signed-ssl
As much as I hate to say this, sometimes it's better to just go with the flow.
Java is attempting to make applications more secure through the use of proper SSL verification practices. In this case, it is succeeding: had you been able to tell the program "it's okay, accept the untrusted self-signed certificate", your program would have been vulnerable to a man-in-the-middle attack where Mallory puts his server (with its own self-signed certificate, just as valid as yours!) in between the host and the target it's attempting to communicate with. Then he proceeds to read all the traffic you thought was nice and safe, and you don't even notice.
So, your assertion that telling Java to "trust any self-signed certificate when connecting to this host" is secure is, regrettably, not correct.
You can get free, totally valid SSL certificates from StartSSL. They're good folks.
That's how PKI is supposed to work - you must have complete chain of trust from some trusted certificate stored in you keystore to your certificate. So you can
set you certificate as trusted
ask somebody already trusted (i.e. with trusted certificate in the keystore) to sign you certificate
Trying to bypass that is not good bad idea. You can install you certificate in some Java post-install hook, you may have some cron job checking it periodically or do it in exception handling. You can even download this way a certificate from the server and install it everytime it changes (extracting the cert with openssl is easy). But for god's sake, if you decide to do such thing, at least write some audit log about it to some third machine a make sure somebody reads it.
You can also write "hacker-friendly" on you doors. :)
(Note that when you're talking about "keystore" in your question, you're in fact talking about the trust store (which is a keystore). More details on this unfortunately confusing Java terminology are in this answer.)
My application will only make calls to a single server so if there was
a way to bypass the self-signed restrictions, and never consult
keystores, it wouldn't be a security problem, but is there a way to do
this?
Actually, it would be a security problem. Your application may indeed be intended to call only a single server, but the trust store is precisely there to help make sure you're connecting to the machine you intended to connect to. Without it, you wouldn't be able to know whether you're connecting to that server or a MITM server.
If you want the security provided by SSL/TLS, don't bypass these rules. (In particular, don't use a trust manager that will accept any certificate.)
I've looked around and the only solutions I've found is to add the
certificate to the java key store, but this is a problem for me
because I have no control over when or how they update java, and I've
read that the keystore isn't preserved between updates, and I have no
access to the system outside of the JVM.
Quoting myself from this answer (to a more specific question):
The right way is to import this self-signed certificate into the client's trust store, using keytool for example:
keytool -import -file server-cert.pem -alias myserver -keystore mytruststore.jks
You can either do it directly in the JRE's trust store (lib/security/cacerts), which may lack some flexibility, or do it in your own copy of this file, which you then set as the trust store (the default password is changeit or changeme on OSX). You configure this truststore globally for your application using the usual javax.net.ssl.trustStore* system properties (e.g. -Djavax.net.ssl.trustStore=mytruststore system property (and -Djavax.net.ssl.trustStorePassword`). [...]
You don't actually have to use the trust store provided by the JRE (and which may be updated regularly). You could import your self-signed cert into a new empty keystore, which you'll use as a trust store within your application (don't import the private key with it). How to discuss trust store management was in fact discussed in this question: nothing prevents you from using a different trust store specifically for your application or part of it (and in fact Sun/Oracle make no guarantee as to the suitability of the default trust store).
I'm building an application that needs to open self-signed HTTPS SSL
URLs in java. I've learned that you can bypass the SSL problems by
adding a call to HttpsURLConnection.setDefaultHostnameVerifier() where
you say what hostnames are allowed.
While it may indeed by slightly less of a problem if you only have a single self-certificate in your trust store, host name verification is also an essential component of the security provided by SSL/TLS. Don't bypass it, verify that the certificate you're connecting to matches the name you're trying to connect to. (To make an analogy, if you want to check someone's identity, you not only want to check the their passport was emitted by a country whose passports you trust, but you'll also want to check they have the right name inside, otherwise you could be in front of anyone from that country.)
Making the CN= RDN of the Subject Distinguished Name of your self-signed certificate be the host name of the server should be enough, although it's the legacy way of doing it. You may also want to add the Subject Alternative Name extension. More on this in this answer.
Generally speaking do not bypass the SSL "problems". These mechanisms are precisely in place to make the usage of SSL/TLS secure.

Categories