how to build a web service client through ssl - java

I'm new to SSL and security and I want to build a java client for a web service that uses SSL. the server is properly configured to use two way ssl configuration, but how to build the client..
also if spring has anything for this it will be good..
thanks in advance

You dont have to do nothing special with the client, just use HTTPS on your request instead HTTP.

to get things work you have to create a client keystore and truststore, then define SSLContext with these stores, then instantiate SSLSocketFactory to produce SSLSockets like this:
SSLContext sslcontext = SSLContexts.createDefault();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, SSLConnectionSocketFactory.STRICT_HOSTNAME_VERIFIER);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
you should read this http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html to understand how it works.
also this question how to write java client and server applications that uses mutual ssl authentication between them? may be useful.

Related

Java use SSLContext for HttpsURLConnection

Looking for an example to open HttpsURLConnection with SSLContext and restricted to TLSv1.2. The context is built using trust store and trust key and after I added the custom() call - the TLS setting seem to be changed to just "TLS" vs. "TLSv1.2"
my code is:
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext = SSLContexts.custom()
.loadTrustMaterial(getKeyStore(trustStoreURL, trustStorePassword), new TrustSelfSignedStrategy())
.loadKeyMaterial(getKeyStore(keyStoreUrl, keyStorePassword), keyStorePassword.toCharArray()).build();
So after the custom() I see "TLS" in sslContext properties.
Why do you want to use only a single version, is there any restriction on your server host ? Most modern servers use TLSv1.2 which is backward compatible to one or two versions.
When you use TLSv1.2 while creating socket factory like below,
SSLSocketFactory.getInstance("TLSv1.2")
the default allowed protocols would be SSL, TLS, TLSv1.1, TLSv1.2.
With that being said, to answer your question,
You can set your SSLSocket to enable just a few protocols using the setEnabledProtocols method. Please check this doc for more on this.
Once done, your SSL connection will allow only the specified protocol.

How make rest service to use only TLS V1.2 using java or spring libraries?

The service provider who is hosting a rest service is asking to communicate using TLS version 1.2 only.
So now I need to make my application to communicate with that service using TLS v 1.2.
I know, in java8 we have an option of disabling the legacy version of TLS and SSL using the property jdk.tls.disabledAlgorithms=SSLv3, RC4 in java.security file.
But in a server there will be other process using the same java settings, so I'm worried like if I change for that property in java.security file, then it will be applicable to other services which are using the same settings.
Question:
I would like to know if there is any other way to make my rest calls use only particular TLS version, through application code using java/spring libraries ?
Create a SSLContext to use the required protocol:
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
If the client code or library uses HttpsURLConnection then you can set the default SSLSocketFactory for all the HTTS Connections:
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
Or at a per connection level:
HttpsURLConnection urlConnection = ....;
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
urlConnection.connect();
Different libraries may have different systems to provide the SSLSocketFactory or SSLContext.
For example, if you are using JAX-RS, you can create a REST client that uses the SSLContext with:
Client client = ClientBuilder.newBuilder().sslContext(sslContext).build();

Wildfly: How to use JAXWS-RI instead of Apache CXF (WebService client only)

My environment is a Maven Project and Wildfly (8.2.1) as Application Server. What I need is to connect wihin a incoming REST call to a third party server using SOAP. I need SSL Client Authentication; therefore, I have my own KeyStore and TrustStore. I create therefore my own SSLContext and need to let the WebService use this SSLContext.
All looks like this:
// Build SSL context with own KeyManager / TrustManager
SSLContext sc = SSLContext.getInstance("TLS");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
String password = "changeit";
ks.load(getClass().getResourceAsStream("/keystore"), password.toCharArray());
kmf.init(ks, password.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
// Now build webservice client
MyWS_Service service = new MyWS_Service(null, new QName("http://...", "MyWS"));
MyWS port = service.getMyWSSOAP();
BindingProvider bindingProvider = (BindingProvider) port;
// set to use own SSLContext
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", sc.getSocketFactory());
// set endpoint
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://hostname:443/.../...");
// perform request
respObj = port.myRequest(myRequestObj);
If I call this code from a JUnit test, all works fine. It uses JAXWS-RI from the JRE.
If I call this code from Wildfly, i.e. from my incoming REST call, where I finally need to fire this request, it does not work because it does not use the own SSLContext. It uses the default SSLContext, which of course is rejected by the third party SOAP server. What I see is that it does not use JAXWS-RI but Apache CXF as JAXWS implementation. So I do guess that bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", sc.getSocketFactory()); is simply ignored [Why?] and has no effect. (I also tried the property name com.sun.xml.ws.transport.https.client.SSLSocketFactory [without internal] - also with no luck.)
I know that I could use HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()) or even use the JVM parameters javax.net.ssl.trustStore, javax.net.ssl.keyStore (and their corresponding password properties). Since this affects all connections, it is out of discussion to use this solution; however, lets look what hapens, if I use it anyway:
JUnit use case: It also works
Wildfly use case: It seems that JAXWS takes the SSLContext, but there is a SSL exception (alert from Server that CA is unknown). This shows that there is even a difference in how to establish the connection. Why is it working, if the code is executed with JUnit? This proofes that the KeyStore / TrustStore is correctly set up with the correct certificates. Isn't it?
Edit:
There is one more proof, that the problem is the JAXWS implementation Wildfly uses: If I just perform a simple HttpsConnection, it even works with my own KeyStore / TrustStore in Wildfly:
url = new URL("https://hostname:443/.../...");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
System.out.println(Utils.inputStreamToString(con.getInputStream()));
So what is the best to do? -> As the question titles, I would like to try to bring Wildfly to also use JAXWS-RI rather than Apache CXF. But I got it not to work until now. I tried to put the following dependency in the pom:
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>2.2.10</version>
</dependency>
But this gives me the following exception:
java.util.ServiceConfigurationError: javax.xml.ws.spi.Provider: Provider com.sun.xml.ws.spi.ProviderImpl could not be instantiated
at java.util.ServiceLoader.fail(ServiceLoader.java:232) ~[?:1.8.0_92]
What is wrong? How can I bring Wildfly to work the same way, as if the code is executed from the same project but "as a JUnit Test"?
Edit:
If you have a tip how to reach the goal (sending SOAP requests using SSL with client auth on Wildfly 8.2.1) in a different way (provided that it is a clean Java EE solution - i.e. not sending own XML bodies :-) and not with too old framworks like Axis 1), it is also welcome! I do need a solution soon - I am fighting for days already...
OK, finally, I gave up to try to replace the JAX-WS implementation used. I got it to correctly setup Apache CXF.
https://stackoverflow.com/a/37268853/4106030
This solve the problem: https://stackoverflow.com/a/46894256/8190026. Wildfly will use the sun implementation instead of apache.cxf
I think it is difficult. There were some comments somewhere about this. By the way, I think WildFly uses RESTEasy, not CXF

Undertow HTTPS listener

I'm trying to serve a simple 'Hello world!' response over HTTPS with self-signed certificate using Undertow embedded server. However I get ERR_SSL_VERSION_OR_CIPHER_MISMATCH on the client (Chrome) and SSLHandshakeException: no cipher suites in common in server logs.
It seems that additionally to KeyManager[] array I need to provide the TLS protocol to use, but I can not found where I should put it. Complete example is avaiable at https://github.com/isopov/undertow-https-test/blob/master/src/main/java/com/sopovs/moradanen/UndertowHttpsTest.java
while at https://github.com/isopov/undertow-https-test/blob/master/src/main/java/com/sopovs/moradanen/JettyHttpsTest.java there is example of similar test using Jetty server (to prove that certificate is good).
This change to code worked for me:
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(getKeyManagers(), null, null);
Undertow.builder().addHttpsListener(10443, "0.0.0.0", sslContext)...

Java Client to connect to Server with Openssl and Client Auth

I have to write a Java Client to connect to an SSL server. The server uses openssl certificate, and is configured to do Client Auth.
I can't seem to locate any useful resources online that can help me (who doesn't know anything about openssl and much about SSL) to understand who to go about implementing my Client Side.
Help!
The twist here is that you are using client authentication, so you need a private key and a certificate to identify yourself. You provide this to JSSE by specifying KeyManagers when you initialize an SSLContext.
Customizable Setup
Following are the basic steps. The JSSE API was significantly improved in Java 6, but I'll stick with Java 5, in case you're stuck on that version.
KeyStore tks = KeyStore.getInstance(KeyStore.getDefaultType());
tks.load(...); /* Load the trust key store with root CAs. */
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(tks);
KeyStore iks = KeyStore.getInstance(KeyStore.getDefaultType());
iks.load(...); /* Load the identity key store with your key/cert. */
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(iks, password);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SocketFactory factory = ctx.getSocketFactory();
Socket socket = factory.createSocket(host, port);
System Configuration
An alternative "zero-config" scenario can be used when using the SunJSSE provider. I believe many other providers (like IBM) have followed the same pattern and will work as well. The mechanism uses system properties, and is described in detail by the JSSE Reference Guide.
For client authentication, the important properties are javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword. The values should be the path to the user's key store and the password for that key store's "key entries", respectively.
When using these properties, you can create a new SSLSocket that supports client authentication like this:
SocketFactory factory = SSLSocketFactory.getDefault();
Socket socket = factory.createSocket(host, port);
Since you are using the "default" SSLSocketFactory, which depends on the system-wide properties, all sockets created in the JVM will authenticate with the same certificate. If you need more control than that, you have to use the "Customizable Setup" above.
Java includes SSL support in the standard API. Have a look at these classes in the 1.5.0 javadoc:
SSLSocket if you're doing the comms logic yourself.
HttpsURLConnection if the server side speaks HTTP
You could use httpclient. Have a look at this SSL guide.

Categories