Consequences of storing TrustedCertEntry and PrivateKeyEntry in JKS? - java

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.

Related

Would having a constant KeyStore make this any less secure?

I'm writing a bit of a net utility, and I want it to have three protocols. PLAIN_TEXT, which, obviously, sends plain text/bytes over the network. ENCRYPTED_UNSECURED, which uses encryption, but doesn't verify any identities, and ENCRYPTED_SECURED, which uses SSL or TSL.
Right now, I've implemented PLAIN_TEXT, and I've partially implemented ENCRYPTED_UNSECURED, but I've hit a block. I'm using SSLSockets for ENCRYPTED_UNSECURED, but with self-signed certs and a fake trust manager, however, the self-signed certs need to come from a key manager, which comes from a keyfile, generated by the java key tool.
What I want to know is: would using the same key file in every instance of the server/client subtract from the security of the encryption? As it is ENCRYPTED_UNSECURED, I don't care about the security of the authentication.
Note: this is being done in java.
EDIT: Why this is not a duplicate?
I'm not trying to import the certificate, I know how to do that, I want to know if using the same certificate could decrease the security of SSL/TSL's encryption protocol.
First, don't use actual SSL; SSLv2 was broken long ago (before 2000), and SSLv3 is now broken in most situations by POODLE (see Wikipedia and/or about a dozen questions on security.SE and crypto.SE). (Note the pseudoprotocol SSLv2Hello in Java/JSSE is not actually SSLv2 and is not a security vulnerability; OTOH since maybe 2010 it is very rarely needed or useful and nowadays more likely to cause confusion.)
Caveat: Java uses an "architecture" for cryptography that allows the same function(s) to be performed by different "providers" depending on how (by whom) your JRE is built and whether your installation has been modified. I will assume you use an Oracle (or formerly Sun) version of Java, or another version that uses the same cryptoproviders, which may include OpenJDK (which is mostly the same source as Oracle/Sun). In particular #Michal's answer seems to assume IBM although I see nothing in your question indicating this; IBM Java although otherwise compatible with Oracle/Sun Java has its own set of cryptoproviders and I don't know if any of the IBM differences affect the issues here.
Finally(!) to your question: distributing one privatekey (keystore) to numerous parties increases the risk of compromise at least proportionally to the number -- perhaps more because people are usually less careful about things that they perceive as someone else's responsibility.
TLS (like SSL before it) supports several different keyexchange options.
Ephemeral Diffie-Hellman in either classic (Zp) form DHE or elliptic-curve form ECDHE. These are usually preferred, and in particular Java/JSSE client orders them first (except for bugs on some early updates of Java7) because they provide Perfect Forward Secrecy; treating now as the beginning of the future -- as the saying goes 'today is the first day of the rest of your life' -- this also preserves confidentiality even if the server privatekey is already compromised, at least assuming the DHE/ECDHE implementation is correct.
Be sure not to use (Zp) DHE if the server runs on Java7 or earlier; those versions used 768-bit DH group which is too small. Java8 defaults to 1024, which is enough except perhaps against top adversaries like NSA, see details at http://weakdh.org; and can be configured for 2048 which is definitely enough, see
How to expand DH key size to 2048 in java 8
or several others. Note Java7 and up (but not 6 out of the box) support both DHE and ECDHE and prefer ECDHE, and Java ECDHE uses adequate curves, so in practice this should not be a problem.
(Plain) RSA. This is not secure if the privatekey is compromised, so in your situation you should if at all possible disable all plain-RSA ciphersuites. Ciphersuites of the form {TLS,SSL}_DHE_RSA_.. and TLS_ECDHE_RSA_... use RSA only for authentication not encryption; it is {SSL,TLS}_RSA_... that you must avoid.
Anonymous. TLS also supports keyexchange with DH or ECDH ephemeral keys but NO AUTHENTICATION (i.e. no certificate at all); these suites are called 'anonymous' instead of ephemeral and have the form {SSL/TLS}_{DH,ECDH}_anon_.... This is actually the exact technical match to your goal of providing confidentiality but not authentication, and needs NO keystore on the server or truststore on the client, plus avoids the hostname issue below. The possible downside is that anonymous suites are disabled by default, precisely because most users expect 'Secure Sockets' or 'Transport Security' to be secure, so you must enable them (at both ends); also if your organization (or your user's if different) has actually thought about a security policy, that thinking fairly often prohibits anonymous TLS for the same reason, and unlike some policy items this one can almost always be enforced by scans or monitors.
Others. TLS also supports nonephemeral aka static DH and ECDH, but practically no one uses them. TLS also supports non-PKC schemes using Kerberos, SRP, or just an arbitrary key (PSK), but these generally are appropriate only in special situations. Of these Java supports static-ECDH and Kerberos; using static-ECDH in your situation would be as insecure as plain-RSA, and IME even in environments where Kerberos is available using it with Java is about as much fun as hitting yourself in the head with hammers.
Hostname check: finally, for (non-anonymous) suites using a certificate, Java SSL/TLS client normally checks that the name used to connect to the server (either a domainname or an IP address) matches the certificate; the certificate must contain either
the name/address of the server, of which there can be more than one using SubjectAltNames aka SAN extension which is supported by keytool beginning in Java7 -- but a full list of names is difficult to arrange if you are going to use one cert on many servers especially ones you didn't identify in advance;
or a name with a 'wildcard' in the first component only: i.e. *.fred.example.com can be used for test1.fred.example.com test9999.fred.example.com anything.fred.example.com but NOT test.john.example.com anything.example.net or even fred.example.com (one level higher).
This check can be overridden, although the details vary some depending on whether you are using HTTPS or not and the Java version. Using anonymous suite(s) as above avoids the issue entirely.

How is the alias name used in Java KeyStore class?

Class KeyStore has a method called setCertificateEntry(alias, certificate). Most client examples I see use "ca" as the alias name. Is the server asking for "ca" automatically during the client-server handshake? What really would happen if I use "abc" instead of "ca?" Regards.
The alias is really just a name that is local to the keystore you are using. It is what identifies the entry in the keystore, so you can't re-use it for two entries, but it can be whatever you like (although I must admit I have never tried with non-ASCII characters, and the official truststore only uses lower case letters or numbers).
The documentation also says:
Whether aliases are case sensitive is implementation dependent. In order to avoid problems, it is recommended not to use aliases in a KeyStore that only differ in case.
Some keystore implementations and formats might have more constraints or use that name differently. For example, the WINDOWS-ROOT keystore (which is a front-end for the Windows native store) uses Windows's "friendly name" as the alias, which is unfortunately not unique in the Windows certificate store, so some certificates from the native store may be hidden and not usable (it's a map from alias to entry, loading a new entry with the same name replaces the other one). However, this shouldn't be a concern on Android, of course.
If you're building a keystore that you'll use as a truststore, which is likely to contain a number of CAs, calling one "ca" would make it difficult to identify them later on. (This is mostly an administrative problem to be able to find manually which cert is where.)
If you look at the default truststore, you'll get aliases with names the resemble the Subject DN of these CA certificates, for example "verisignclass1g2ca".
Having an identifier you can remember is generally more important for keystores that are used as keystores (as opposed to truststores) and which contain multiple private key entries, since this can help you configure your application to use a particular certificate to identify itself.

Is it a bad practice to use Java standard keystore

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.

Adding certificates used by URL.openStream

I have some code on Android that makes use of URL.openStream. For internal test purposes I need to be able to point it to a server that uses a private CA. We already have our CA bundles in PEM format (as we're also using libcurl from NDK), and would like to be able to just read the PEM files directly into whatever KeyStore (or similar) that URL.openStream uses.
So this question is a multi-part thing:
How do you get the key storage used by URL.openStream? Or should I just be using HttpClient directly?
How do you add a PEM certificate to said key storage? (even if using HttpClient)
Thanks.
I can't speak for Android specifically, but at least standard desktop Java has a default keystore that is used by all instances of the JVM, located at /lib/security/cacerts.
In many cases, this file should not be modified globally for all instances of the JVM, but on a case-by-case basis, as you already eluded to. To do this, you can't call .openStream directly. Instead, get a HttpsURLConnnection by calling URLConnection.openConnection() (and casting it do a HttpsURLConnection). Before performing any other operations on this connection, set a custom SSLSocketFactory by calling HttpsURLConnection.setSSLSocketFactory. From here, you'll need to work with a custom SSLContext and TrustManagers.
Some additional details around this are already answered at How can I use different certificates on specific connections?.
If you want to use HttpClient, additional references are available at http://hc.apache.org/httpclient-3.x/sslguide.html and How to handle invalid SSL certificates with Apache HttpClient?.

Accessing SSL Private Key From a Servlet

I'm running Apache Tomcat locally and have installed a self signed certificate using OpenSSL. I also have a servlet running.
What I want to do is the following:
The servlet takes in POST parameters and will echo back the parameters signed using the PrivateKey of the server. That is, treat the input parameter as an integer and raise it to the power of the private key.
The problem: Is it even possible to access the server's PrivateKey from the servlet? If deployed on some other hosting, it's probably not possible to access it directly. So is there any way that I can request that the server 'sign' some piece of data with its Private key?
Thanks in advance.
If I were writing a weberver I'd go to great lengths to prevent code from reading from the certificate store - I certainly wouldn't provide an API call for it!
Usually the cert is stored as a file on the webserver's filesystem (not always) but for a site with any serious security on it, this would be encrypted and require a passphrase to decrypt. Since you are using a self-signed certificate its probably not encrypted - just import the cert from the file into the keystore.
This depends on the way the container is configured. In the general case, the SSL configuration of the container will not be accessible by a servlet it contains. For example, in Apache Tomcat, that connector's SSL configuration can be completely independent of the settings accessible by a servlet. (In addition, SSL may be handled by APR or an Apache Httpd front-end, for example, which won't have much to do with the Java keystore configuration).
You would probably be able to gain access to the keystore if they are configured using the javax.net.ssl.* properties. However, that's not necessarily the way the SSLContext of the server connector is configured from. It's not generally a good idea to pass those parameters on the command line either in a production environment.
What you may be able to do is to load the keystore used by the container, from your servlet, but you'd have to know where it is, in advance. In addition, I would suppose that a hosting service would run Tomcat with a security manager turned on, which may prevent you from doing this.
Note that you could use your own keystore, shipped within your webapp (it can even be loaded from the classpath as a resource stream). Whether this keystore will contain the same private key as the one use by the server's SSL layer is up to the server's administrators (if they let you have it).
If you use a Java keystore you can access it from your servlet, as long as it knows the keystore location and password.
Servlet can access any data on your machine including keys. The only situation can be when Apache and Tomcat are running from different user names and keys are locked to be accessed only by Apache user name. You can do a trick in this case as su a process under Apache user name and read the file.

Categories