My Java program uses GitHub's raw address to access a version file to get the latest version. This address is in the format https://raw.github.com/user/repository/branch/version_file
During testing stages, I had no problems using this with the following code:
currentVersion = new Version(plugin.getDescription().getVersion());
URL url = new URL("https://raw.github.com/zonedabone/CommandSigns/master/VERSION");
URLConnection connection = url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
newestVersion = new Version(in.readLine());
if (currentVersion.compareTo(newestVersion) < 0)
newAvailable = true;
However, some users have complained of the following error:
sun.security.validator.ValidatorException:
PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
Java is complaining that it can't validate the SSL certificate. GitHub's certificate is verified by DigiCert, but apparently some Java builds won't identify this.
I have read there are two ways to overcome this: adding the certificate to the TrustStore and disabling the validation altogether.
The answers suggested on StackOverflow either make use of an allow-all TrustStore, which would be a really bad idea considering it's not within bounds of a 'test environment', or if they show how to add the certificate, they usually link to a broken web page.
Can somebody provide new information?
First the theory... As the JSSE Reference Guide says:
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.
Users should maintain the list of CA certificates themselves. A number of people won't know how to do it, unfortunately. They could use another JRE's cacerts file (more recent) into their own installation.
You could use a trust manager that trusts any certificate and use it specifically for that connection, but that's not a good idea, since it opens the connection to potential MITM attacks. There are enough examples of code for this insecure solution around.
The best solution, if you want to trust a specific certificate, is to load it correctly using a specific truststore, as described in this answer. You could even load this trust store from an InputStream from the classloader if you want to bundle it with your application.
Alternatively, where possible, you could use the OS trust store directly. On Windows, you can load the Windows-ROOT keystore as described in this article. On OSX, you can load a KeychainStore.
You can implement an HttpsUrlConnection with "all"-TrustManager
like http://bitsandcodes.blogspot.de/2010/08/create-trust-manager-that-trust-all-ssl.html
Related
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.
I am building an application in Java which I would like to make rest calls to https services. The application is written in Java Standard Edition.
I am using If This Then That as a test but I want the application to connect to any https site that the user types in.
For IFTTT I use the following URL:
https://maker.ifttt.com/trigger/{my_event} /with/key/{my_app_key}
I get the following error:
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
I think this error means that the root certificate used for IFTTT is not in my cacerts truststore. I understand a solution to this problem may be to add it. According to chrome the certificate path terminates at a root certificate names “Go Daddy Root Certificate Authority – G2”.
I guess if I add this to my cacerts using keytool it will work. However I expect users of my application to type in various URL’s. I don’t want to only allow a particular subset of the internet. I also don’t want to disable certificate checking.
Chrome, IE and firefox all recognise the IFTTT site without a problem.
Why doesn’t the standard cacerts file in Java recognise this?
Can I get Java to use a system cacerts file which includes all these sites? (This would have to work on Java on Windows, Max and various Linux flavours.)
In a Java desktop application I need to connect to a SSL URL. When I try it, I get the exception:
SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
To solve it, I go to the address with my browser, download the certificate in .cert file and import in my java cacerts keystore using keytool command. After this, the application works fine.
My question is: why java don't recognize the certificate if this is signed with VeriSign? VeriSign certificate is not installed in cacerts keystore by default?
Maybe I don't understand how SSL works. What's the difference between browser and java desktop behavior? I can connect to this URL with my browser without installing nothing.
Thanks
When we access a secure site which uses SSL for providing identity and encryption, it provides a certificates which was verified by a trusted third party sites like verisign, godaddy or thwate.
By using certificates browser or java clients knows that they talking to the correct site (who it claims to be) and not on redirected proxy site. this step is pretty transparent if you access websites using browser because if certificate is not on browser's trusted store it will ask you to add that certificate and it will be subsequently added.
But when you access a secure site using Java program, this step of certificate hand shaking is not transparent to user and certificates are verified form JRE's trustStore.
Read more: http://javarevisited.blogspot.com/2012/03/add-list-certficates-java-keystore.html#ixzz32v1wL3Gl
You did not say which certificate you imported into the keystore. Usually there are several certificates involved in the verification:
the trusted root certificate, which is stored in the keystore or browser (Java does not share the keystore with the browser)
the leaf certificate which identifies the site
and most times also intermediate certificates, which provide a signed way from the leaf certificate to the trusted root and thus establish a trusted path
If any of the intermediate certificates is missing, the verification fails.
So the server has to send not only the leaf certificate, but all intermediate certificates too. And this is where lots of sites fail, e.g. they don't provide the full certificate chain.
But why does this work in the browser?
Because enough sites fail to provide the intermediate certificates and because the browser wants to provide the best experience, they will cache the intermediate certificates. Thus, if you go to one site signed by verisign which provides the trusted chain, and then go to another site signed with the same certificate, but which does not provide the chain, then it will still work, because the chain certificates are cached from the other side.
But, if you use a fresh system (or simply a new browser profile with firefox) and visit the misconfigured site first, it will complain there too, as much as the Java application did. And if you just import the relevant intermediate certificate into the keystore and thus trust it it will work too, because it does not need the rest of the chain to get a path to a trusted certificate.
I'm trying to access https://blockchain.info/lastblock from java,
but the code throws:
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
This is usually due to self signed certificate, but this one is not self signed(you can check it in your browser). Looks like java doesn't user system CA certificates.
Isn't it a bug?
I exported the certificate and insert it dynamically into java KeyStore, but I don't like this solution(Not sure it will check revocation lists for this certificate etc).
So what's the best solution to fix the problem?
I used this code to test http://www.cs.ucsb.edu/~pconrad/cs56/examples/ldap/SimpleQuery/InstallCert.java
The list of roots of trust changes from time to time, and Java's built-in one is often a little different to that of browsers. Those trust roots are what is stored in the system keystore (well, technically in the system truststore) and you can add to them by placing the extra roots that you want to support in a per-user keystore, stored in your home directory. It's in the default location manipulated with keytool.
The certificate that you'd actually consider putting there — if it isn't already — would be the “COMODO Certification Authority” one; browsers like it (i.e., you can verify it independently of Java) but your Java doesn't. It has revokation information signed directly into it, so it should be all you need; SSL/PKI is supposed to be able to be bootstrapped from just the root CA certificates.
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.