I've been developing a WS client using JAVA and I'm having a problem with SSL authentication. The WS are created on WCF and I have no access to the server, they work through HTTPS and uses a client certificate that needs to be installed on the client first. The server guys sent me a PFX certificate which I successfully installed on the OS (I'm using OS X) and I could then access the WS via a browser (Safari or FF are both that I tried which previously couldn't access the WSs).
I thought any app in the OS would use this certs but when I'm tried my JAVA app it didn't work; at first the following error was being thrown:
"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 solved this by exporting the certificate to a CER file and using the keytool command line tool to add the certificate into the "cacerts" keyStore JAVA uses. But after this error went away the following started appearing: "403, forbidden". This is obviously because it's not using the SSL client cert for the site but I haven't been able to find a way to send it to it. Any help would be appreciated.
The following is the code I use to post to the WS:
URL url = new URL(p_url);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", contentType);
OutputStream out = conn.getOutputStream(); // on this line it shows the error
You can either create a specific SSLContext (using a KeyManager initialised with the keystore containing your client cert + private key), from which you derive an SSLSocketFactory, which you set into your HttpsURLConnection, or use the global settings.
You could set the following system properties (for the global settings):
javax.net.ssl.keyStore=path/to/keystore.pfx
javax.net.ssl.keyStoreType=PKCS12
javax.net.ssl.keyStorePassword=xxxxxxxxx
Alternatively, you can create your own KeyManagerFactory/KeyManager as described in this answer.
Since you've imported the server certificate in your cacerts, use null for the TrustManager[] argument of SSLContext.init() (it will pick up the default values).
In addition, since you're on OSX, you could use the KeychainStore directly. To do so, use ....keyStore=NONE, keyStoreType=KeychainStore and keyStorePassword=- (any password will do, since access to the key will be granted when you need it from the OS). I'm not sure if it works on Lion, though. Note that it may fail if you have more than one cert+private key in your store (see this issue).
Looks like you probably need to set up your own SSL SocketFactory,
http://vafer.org/blog/20061010073725/
I would think things have gotten better since 2006, so you may just need to specify a bunch of properties on the command line:
http://stackoverflow.com/questions/875467/java-client-certificates-over-https-ssl
You need to load the keystore they send you in your java application.
You can load it as a file from the file system in a Keystore object and use it. Read this example and especially the part about KeyManager i.e. createKeyManagers method.
Another option would be to load the keystore from windows. Read about Windows-MY provider
Related
I have two apps, one is written in nodejs (which in this case is simple mock-server) and spring app (core app). I want to create a SSL connection between this two. Firstly I generated pair of key and cert, then simply setup a nodejs to work over SSL (I used this example: setting SSL - nodeJs).
I have checked that when I wrote in browser https://localhost:3000 it gives me a "secure" connection (with open padlock - so it's ok).
Next I was making a configuration on spring app using setting SSL - Spring. I took a cert from previous step generation and set it in keystore into .p12 file. In spring application I changed the url to node app to https.
When I try to connect from spring app and get some mock data from node I'm getting this stacktrace:
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
I found something in stack but when I set this cert in specific directory and then restart my app, result is same.
Have anyone maybe a clue what could be wrong in this configuration and some suggestion how to fix it?
I found the solution of this problem. I don't know why, but when I created a keystore with cert inside and then use it in my spring app it cannot find this keystore with cert globally. I was looking for documentation about openJDK11 (because I used it) and find a link to Enabling Unlimited Strength Jurisdiction Policy and check file for which I have version of openJDK11.
In my case the line from crypto.policy was commented, don't know why. When I uncommented it and set unlimited it gives me one more error, but then I find one another solution. I found that, that I need to add my sign-self certificate to cacerts file (how to add my self-signed cert to cacerts). After this two operation it works as charm.
I have Mule ESB deployed to a Linux server and Anypoint Studio running in my local windows dev environment. I have a fairly simple flow that includes a SalesForce connector. Salesforce REST API has a valid cert signed by VeriSign and my local instance of Mule happily accepts it and connects with no problems. However, when Mule runs on the server it always throws "PKIX path building failed, unable to find valid certification path to requested target".
I've tried using the default java keystore, specifying a keystore with javax.net.ssl.keystore, nothing works.
On my local machine I've ended up doing the following to show that Mule is using the right trust store:
I generated an empty truststore and added it to my AnyPoint project as a resource. I created an HTTPS connector configuration in order to explicitly specify the truststore I want to be used for my SalesForce connector, and pointed it to my empty truststore. When I try to run the project locally, I get the exact same SSL error (as I would expect, since it's an empty truststore). I then take the VeriSign CA cert and add it to my empty truststore. After that, locally everything works just fine. To me, this confirms that my mule project is using the truststore that I've added as a resource to the project itself.
I then export this project and deploy it to my server. On the server it throws the SSL error.
Can there be some weird JVM config differences that could cause this?
It turns out that Mule does not use a different trust store for each service. If you have multiple services deployed to it, the last service to declare a trust store explicitly will force all other services to use that same trust store, overwriting whatever configuration they may have. This was happening in my case. I found this out by echoing out System.getProperty("javax.net.ssl.trustStore"); to log and realized it's a trust store that was a resource in some completely different deployed project but being used by mine.
Seems like a pretty bad screw up by the Mule guys.
I agree with your suspicion that the JDK on your linux server doesn't trust the proper certificates. However, this doesn't need to stop your application from doing so.
I've been able to make the salesforce connector trust a given certificate by doing the following:
KeyStore truststore = KeyStore.getInstance("JKS");
truststore.load(myKeystoreInputStream, myKeystorePassword.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(truststore);
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, tmf.getTrustManagers(), null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
The key is the last line. The salesforce connector uses HttpsURLConnection directly to connect to the server, while the HTTPS connector doesn't. This will allow your mule application to use a different trust store for salesforce than it uses for one or more HTTPS connectors. You can use this to control the SSL certificates that your application will trust, independently from the certificates that the server's JVM trusts.
I always have to import a new certificate when ever I delete a server and create a new one for my webapplication.
cant the same certificate be used ?Tried using it but gives a PKIX error.
Server is localhost tomcat.Certificates have been imported in Cacerts.
Certificate is selfsigned.
I think it can be used. I used the same certificate on different tomcats. But please but your ceacerts into your java jdk/jre/lib/security/
and also create tomcat crt.
http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html
I am given a certificate by my api provider which are .crt file and .p12 file.
I generated ca-cert.pem file from .crt file and
client-cert.pem and client-key.pem file from .p12 file using openssl as required by api.
I am also provided with username password for basic authentication by my api provider.
My question is how can I connect to server url https (rest) using these certificate and credentials using Java.
You basically have to create an SSLSocketFactory see here for an example. Once you've created the SSLSocketFactory you can set it when you create a Connection from the URL like so.
SSLSocketFactory sf = ... see that example
URL url = new URL("https://google.com");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(sf);
... Now do stuff with that connection like GET or POST
Also fyi, you are going to want to convert your certificates into the JKS format that Java prefers in order to easily load them into the SSLSocketFactory. You can use a program called keytool that comes with java which can help you out.
I'm trying to print a URL (without having a browser involved at all) but the URL is currently throwing the following:
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'm calling the URL using a JEditorPane's setPage method, which just takes a URL as a parameter. Assuming I can't change anything server side and I still need to get to this resource, how would I go about ignoring the certificate error (or something else that gets me to my goal)?
Accessing this URL via a browser tells me the site is untrusted and asks me if I want to proceed.
Extend JEditorPane to override the getStream() method.
Inside that method, you can open a URLConnection. Test whether it is an HttpsURLConnection. If it is, initialize your own SSLContext with a custom X509TrustManager that doesn't perform any checks. Get the context's SSLSocketFactory and set it as the socket factory for the connection. Then return the InputStream from the connection.
This will defeat any attempts by the runtime to protect the user from a spoof site serving up malware. If that's really what you want…
This is possibly because your certificate in your keystore for accessing target HTTPS URL does not match the certificate from server.
You need to import the certificate in to your JVM's keystore.
Obtain keystore certificatesto access this URL and then importit into the main keystore with
keytool -importkeystore -srckeystore /path/to/custom/keystore -destkeystore $JAVA_HOME/jre/lib/security/cacerts
Assuming you are using Java from $JAVA_HOME
I would use erickson solution.
Another way is to add server's certificate (probably self signed) to your trusted certificates KeyStore (I would do it just in your test enviroment).
Use:
java InstallCert YourHost
to create a jssecacerts file
then copy it to the following folder:
$JAVA_HOME/jre/lib/security
If it is the same server that you will be contacting each time, it might be okay to simply trust this one certificate by adding it to the trust store. I wouldn't add this to the default cacerts file, but you can make your own keystore and specify it through the javax.net.ssl.trustStore system property.
Lastly, if you want to disable PKIX checking completely (which you should only do if you understand that this will compromise security quite a lot) the only way is to implement your own SSL context and trust manager as erickson has suggested.
On a related note: my mutual certifcate authentication with a WCF .net Web Service was causing issues in my test environment. I added these two lines to my code to fix the problem and allow me to work through the issue:
//Uncomment this in case server demands some unsafe operations
System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
//this command allowed me to see the detailed debugger log for detecting the
//issue with my certs.
System.setProperty("javax.net.debug","all");
Here is a nice reference for dealing with SSL negotiation and certifcates in a Java/Windows/Unix world: Transport Layer Security (TLS) Renegotiation Issue Readme