I'm working on an application that calls a third-party webservice over https. So I need to add this certificate to the truststore of my application. I can see 3 solutions to fix my problem:
add this certificate to $JAVA_HOME/jre/lib/security/cacerts
create a custom truststore and launch my JVM with -Djavax.net.ssl.trustStore= ...
programatically load this truststore when starting my application
Which solution do you recommend/discourage me to use?
I'd prefer the second one. Because;
For the first one; when you change your java version you need to do extra work (you must add these ssl certs to cacerts again).
For the third one; when you need to add another ssl cert. you must change your code.
So, the second is the best choice because; you will not need to change your code when new ssl comes (You will just add it to external trustStore) and you will do nothing for these certs when you upgrade your java version.
Related
I would like to understand the Java runtime's requirement for SSL certificates storage in general.
I understand it can be copied to the host's /etc/ssl/certs folder but for Java, does it need to import to a specific Keystore for a runtime to be able to use and consume in any SSL verification process by the application?
E.g.
If I have a JRE client that requires packaging of a root/intermediate certificates to make web client internally to site1.foo.com, I will need the root and intermediate certificates dependent on the chain to verify the request.
With various other runtime environments, it seems I can just place them in the /etc/ssl/certs folder:
NodeJS => How to add custom certificate authority (CA) to nodejs
Go => Where is Golang picking up root CAs from?
However, presumably for usage in Java, I need to go an extra step and use keytool and import into a specific Keystore?
Presumably, it can't just pick up from a common directory as per above?
Hope my question makes sense.
In Java, collections of certificates are usually accessed through a KeyStore interface.
As remarked in the comments the default SSLContext will read the certificates from a PKCS12 (or JKS) file located in $JRE_HOME/lib/security/cacerts.
However that is not the only possibility and you don't have to call keytool to add trusted certificates:
on Debianoids you can use -Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts (cf. this question) to use the the PKCS12 file provided by the ca-certificates-java package. It is updated whenever you call update-ca-certificates. Therefore you just need to add a *.crt file in /usr/local/share/ca-certificates and run update-ca-certificates.
if you don't use the default SSLContext you can init it with a different TrustManager (cf. this question). That's how Tomcat 8.5+ loads certificates in PEM format.
Unfortunately there is no implementation of KeyStore that reads certificates from a directory, but that can easily be written.
Edit: On Debianoids the packaged JREs already use /etc/ssl/java/cacerts, so no further configuration is needed.
The server has changed from http to https. Instead of modifying client code (of course the host which was http is now https), we use keytool to save server CA. When I used keytool, I see a list of all stored CA (about 100 pieces).
So how does JRE know which CA to use? And what is the story behind this, like why when JRE is configured with a CA the client code could remain the same?
Thanks.
I'm trying to answer the basic questions you pose.
Java and Keytool know which CAs to trust by consulting a special file named cacerts which is shipped with the JRE and it's placed in <JRE_HOME>/lib/security.
This kind of file is usually called KeyStore or TrustStore.
This file is shipped by default by the "creator" of the JRE (e.g. Oracle) and contains list of Certificate Authority certificates to be trusted (usually updated at every release).
It's not that different from the same process for browsers.
As for the story behind this, if I understand correctly, we are talking about PKI (Public Key Infrastructure) which, in you case, very roughly, means that when your JRE keystore gets updated with the new CA certificate, then entities (such as SSL Sites, applets,etc) presenting a certificate provided (usually sold) by that CA are now basically valid and trusted, since now a trusted third party (the new CA) can guarantee (by means of encryption and digital signature) that "you are effectively you" in a digital scenario.
Also worth noting that, Keytool can be instructed to use a user-defined keystore with the -keystore <keystore_path> command line option (and even the JVM can configure a specific non-default truststore for SSL connection via a system property like this)
I have a Java web app that has been running fine for several months. It integrates with the Box API (https://upload.box.com/api/2.0) to save files to the cloud service. Out of the blue, we started receiving the dreaded javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated exception while trying to make a REST API call to the Box web service.
There are tons of posts out there that tell you how to manually import the cert into your key store using the keytool utility. I understand that I can do this to fix the problem. But is this really the correct fix?
The fact that my application has been running fine for months leads me to believe something in the certificate at https://upload.box.com changed. Looking at the cert in my web browser, the certificate seems valid and was only renewed a few weeks ago. What is missing from my keystore?
Is it the Root CA certificate that is missing from my keystore? If that is the case, could I just copy the cacerts file from newer version of Java? My app is currently running JDK 1.6.0_33.
I am just trying to understand why this would suddenly stop working and what the "real" fix should be. It doesn't seem like modifying the JDK keystore is the correct thing to do.
I'll just assume you're using Apache HTTP Client 4.x, before 4.2.6, 4.3 Beta2, in which case javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated is most likely to come from a certificate that isn't trusted (otherwise it means the server didn't send a cert, which is a different problem, more details in this answer).
The current cert for server you're trying to access seems to have been issued on 07/04/2014, so this indicates that the certificate, and perhaps its chain has changed recently indeed.
I don't have a JDK 1.6.0_33 at hand, but it's possible that some of these CA certs were not part of the default bundle. In any case, it's worth updating cacerts, even on older JREs (if only to remove CA certs that should no longer be trusted, for example). The JSSE Reference Guide clearly states (admittedly in the middle of a fairly long document, but it's worth searching for "important note"...):
IMPORTANT NOTE: The JDK ships with a limited number of trusted root certificates in the <java-home>/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.
If you can't upgrade your JRE (Java 6 is in general out of support), updating the cacerts file from a more recent version is certainly a sensible compromise.
Besides the various fixes in Java 7, Java 7+ would also allow you to connect to hosts that require SNI (although this doesn't seem to be the case for this particular host).
I'd like to know is there any way to check default keystore if necessary key is not found in specified keystore or how to set keystores checking order.
For example - I have two keystores:
Global java default keystore (like cacerts)
"Folder1/mykestore.jks" which contains my uploaded self-signed certificates.
I set property in my program:
System.setProperty("javax.net.ssl.keyStore","Folder1/mykeystore.jks");
And programs tries to connect with certificate from mykeystore.jks, but doesn't found it, so throws exception. Now I'd like to oblige my program to check also global default keystore or any other keystore if exists.
I ned to do it in my application, where user can defines some mailboxes and can upload self-signed certificates. Application works good with self-signed certificates, but doesn't works with public mailboxes (like GMail) with official certs, because they are not exists in mykestore.jks. (When I try to connect without specify keyStore property, it connects without any problems)
Probably the easiest way is something like:
try{
System.setProperty("javax.net.ssl.keyStore","Folder1/mykeystore.jks");
store.connect(host,login,password)
catch{
//Set default keystore
//try store.connect() again
}
but the problem is I dont know how to set default java keystore (it is an gralils app, and I don't know path to default keystore).
Maybe is there another, better way, you could recomend me?
Regards,
Artur
The simplest thing would be to just make a copy of the default keystore from the JDK and use that as the initial keystore for your application, adding more certificates to it as necessary.
I've got a java applet that loads some pre-installed native code extensions to display custom content in the browser. Some of this content may include native code to be loaded by the JVM. Obviously, this is a security concern. I'd like to enforce that all content comes only from authorized servers.
The path I've been following to accomplish this is to create a keystore that contains just one SSL certificate. I set the keystore location and password and turned on debug output.
System.setProperty("javax.net.ssl.keyStore", "C:\\keys\\keystore");
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
System.setProperty("javax.net.debug", "ssl");
I was under the impression that this would mean that the JVM would have access to only the one keystore file and consequently the one key inside it. In fact, the SSL debug info lists something like 75 CA keys in addition to the one key I added. Clearly, this isn't going to keep anyone from sending us untrusted code.
Is there a way to tell the SSL system to only use a single certificate? Should I be using a completely different approach?
Update:
Changing the cacerts file isn't really an option here. The JVM should continue to function normally for other applications using it. Is there a way, at runtime, to elect not to load that file? I'm looking at the TrustManager and KeyManager classes but I don't really understand how to use them.
You need to set the javax.net.ssl.trustStore system property to point to a keystore with your one certificate in it. The keystore is for your authentication credentials, not your peer's.
The very question is wrong here. A certificate is only a proof of identity. You don't authorize certificates, you authorize identities. What if the same client comes up with a new certificate?
The correct answer is to install a HandshakeCompletedListener that checks the peer identity. The truststore is only there for authentication, i.e. is that person who they said they were. What you are doing is authorization, which is a different thing completely. You shouldn't use the truststore (or any PKI mechanism) for authorization.
You also have the global keystore installed with the JRE, which is where all the CA's are stored. Try to rename it and see what happens.
You still "see" the CA certificates of the system-wide JRE cacerts file located in java.home/jre/lib/security, which is normal.
Now, quoting the keytool documentation about this file:
IMPORTANT: Verify Your cacerts File
Since you trust the CAs in
the cacerts file as entities for
signing and issuing certificates to
other entities, you must manage the
cacerts file carefully. The cacerts
file should contain only certificates
of the CAs you trust. It is your
responsibility to verify the trusted
root CA certificates bundled in the
cacerts file and make your own trust
decisions. To remove an untrusted CA
certificate from the cacerts file, use
the delete option of the keytool
command. You can find the cacerts file
in the JRE installation directory.
Contact your system administrator if
you do not have permission to edit
this file.
In your case, it might be easier to entirely replace the cacerts with your own key store instead of removing all 75 entries if this is really what you want. If not, then you should use a different approach indeed (why don't you just restrict or hard code the list of authorized servers?).