Is it possible to easily attach a client certificate to a Axis2 stub generated using wsdl2java? I need to change the client certificate dynamically on a per-request basis, so simply storing it in the keystore won't work for our case.
I've found examples where this is being done for non-SOAP calls, but could not find anything related to using the Axis client stubs. Trying to hack the XML for the SOAP call is an option I guess, albiet a painful one! Groan!
If you want to change which certificate is used depending on which connection is made, you'll need to configure an SSLContext to do so, as described in this answer: https://stackoverflow.com/a/3713147/372643
As far as I know, Axis 2 uses Apache HttpClient 3.x, so you'll need to follow its way of configuring the SSLContext (and X509KeyManager if needed).
The easiest way might be to configure Apache HttpClient's global https protocol handler with your SSLContext, set up with an X509KeyManager configured to choose the client certificate as you require (via chooseClientAlias).
If the issuers and the connected Socket (probably the remote address) are not enough for deciding which certificate to choose, you may need to implement a more complex logic which will almost inevitably require careful synchronization with the rest of your application.
EDIT:
Once you've built your SSLContext and X509KeyManager, you need to pass them to Apache HttpClient 3.x. For this, you can build your own SecureProtocolSocketFactory, which will build the socket from this SSLContext (via an SSLSocketFactory, see SSLContext methods). There are examples in the Apache HttpClient 3.x SSL guide. Avoid EasySSLProtocolSocketFactory, since it won't check any server cert (thereby allowing for MITM attacks). You could also try this implementation.
Note that you only really need to customize your X509KeyManager, you can initialize your SSLContext (via init) with null for the other parameters to keep the default values (in particular the default trust settings).
Then, "install" this SecureProtocolSocketFactory globally for Apache HttpClient 3.x using something like this:
Protocol.registerProtocol("https", new Protocol("https",
(ProtocolSocketFactory)secureProtocolSocketFactory, 443));
Related
this question might sound I bit dummy but I have researched many questions/answers here and can't find the answer for my case.
Currently I am using RestTemplate library to make HTTP requests for my java library that I am currently working on. In order to have successful HTTP call to HTTPS URLs i needed to add a SSL configuration for my HTTP client. Something like this:
clientBuilder
.disableCookieManagement()
.setDefaultRequestConfig(requestConfig)
.setSSLSocketFactory(new SSLConnectionSocketFactory(SSLContexts.custom()
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
.build()));
So this library is supposed to be shipped to the user as a jar executable application and I know that using self-signed SSL certificates is not a good idea for general usage since there might have web servers that do not acknowledge it. I read that If I get a signed SSL certificate then I should save the proper keys on Keystore and also use Trustore to acknowledge the server's certificate. I do not think I can just pass Keystore and Trustore to the client who executes my java library, so my question here is, does Java has a built-in SSL certificate, so I could somehow just do some configuration on HTTP client and the built-in certificates would be used. As far as I know, node.js offers something like that.
Could anyone just give me a explanation of how this works for java spring-boot?
There are two separate certificate verifications that could be happening. To connect to a server using https, you need to receive the server's certificate and validate it using a truststore.
In addition, it is possible for you to have a client certificate, and to pass that to the server so it can authenticate your client. Unless you have been told you need to do that, you don't. Unless the server has been specifically configured to do it, it isn't possible. If it is what you need to do, you need to obtain a client certificate, install it into a keystore and use that keystore in your client.
So for normal https, you do not need a keystore.
Whether you need "TrustSelfSignedStrategy" depends on the server.
If the server has a valid signed SSL certificate, you do not need to do anything special, RestTemplate will just work.
If the server has a self-signed certificate, you need to either configure the client to accept any self-signed certificate, or load the server's certificate into a truststore so the client knows to accept that specific certificate.
I'm receiving the ever so popular "SSLHandShakeException".
I'm running a java client using IntelliJ that is designed to handle web service requests/responses. We connect to server through a set of credentials and a url and pass in a request file.
What I've done
Verified the URL being constructed works in a browser.
Added the certificate to the trust store using these
instructions, Keytool Instructions
Verified the correct JRE is being used for the truststore.
I'm using an httpClient object. I instantiate the object as follows,
private void initConnection()
{
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(this.target.getHostName(), this.target.getPort()), new UsernamePasswordCredentials(this.userid, this.password));
this.httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
// Be able to deal with basic auth. Generate BASIC scheme object and
// add it to the local auth cache
BasicScheme basicScheme = new BasicScheme(); // we throw an error here
this.authCache.put(this.target, basicScheme);
localContext = HttpClientContext.create();
localContext.setAuthCache(this.authCache);
}
The execute for this object is as follows which is also the code throwing the error,
responseNode = this.httpClient.execute(getTarget(), httpRequest, responseHandler);
I've tried forcing the trust store in the process as follows,
System.setProperty("javax.net.ssl.trustStore", "....Common\JRE\lib\security\cacerts");
System.setProperty("javax.net.ssl.trustStorePassword","changeit");
Works just fine against http when SSL is not involved.
Other then that I'm not an expert with SSL. I've performed quite a bit of digging and I am hoping someone out there has an idea. I'm certainly open to having not installed the cert properly (dozen times) or possibly I need to add some more code to correctly configure my objects. If there is missing information I would be happy to provide it!
Thanks well in advance.
Having had to deal with a number of SSL issues in Java clients myself I understand the frustration with the lack of useful detail in the error messages you are experiencing. Here are a few things you can try and verify.
That the server and the client can agree on the SSL version.
Like many protocols the SSL/TLS protocol has several versions out there. TLS implementations are practically always backwards compatible, but since the earlier versions of the protocol are considered insecure many servers and clients will not accept earlier version.
Hostname mismatch.
The TLS protocol specifies that even though a server's certificate might be trusted the certificate itself the common name of the certificate must match the hostname of the server. For example if a server at www.microsoft.com provided a www.google.com certificate the client would not accept the connection. This issue tends to be a big problem when people are connecting to internal servers, but not so much a problem when connecting to public servers. There is a pretty simple way to disable hostname verification with a custom SSL factory.
By using the custom HttpClient it is not looking at the system trust store by default from HttpClientBuilder docs:
When a particular component is not explicitly set this class will use its default implementation. System properties will be taken into account when configuring the default implementations when useSystemProperties() method is called prior to calling build().
Try using
HttpClients.custom().setDefaultCredentialsProvider(credsProvider).useSystemProperties().build();
The short version: Is there a way to make HttpsUrlConnections started inside a Java Web Start app trust the certificates installed in the Java Control Panel, or is there another user-friendly way (not involving the command line) to add certain certificates to the trust store used by such connections?
Our Java Web Start application accesses several web services (via JAX-RS/Jersey in standard configuration, which ultimately uses the JWS-specific implementation of HttpsUrlConnection). Occasionally these will throw SSL certificate validation errors due to untrusted certificates:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
These can occur for several legitimate reasons:
In our test environments, the site certificates are issued by our internal CA which is not trusted by the JRE by default
Some of our users have "SSL terminating" proxies which is a fancy name for a web proxy that decrypts SSL traffic and forwards it on using a different certificate. This is indistinguishable from a man-in-the-middle attack from the app's point of view.
After upgrading servers in production we test the app by launching it from the individual servers before adding them to the load balancer; the certs for these servers are also signed by our internal CA.
At the moment we have code that picks up on these errors, shows the user a suitably stern-worded warning and, if the user wishes to proceed, installs an "accept-all" default trust manager and hostname verifier to HttpsUrlConnection to bypass SSL validation entirely, as widely documented elsewhere. This is a cumbersome solution, it is hard to maintain, it appears on every execution of the app, it will mask other errors (we connect to more than one site) and feels like re-inventing the wheel.
An alternative is to use keytool to add the untrusted CA certificate to the JVM's trust store. This is not a very user-friendly solution as it involves a lot of command-line hackery and will be wiped out after every JRE upgrade.
I would expect adding these certificates to the Java control panel would also achieve the same thing but it seemingly does not; although it gets rid of the Java Web Start warnings when installing and launching the app, HttpsUrlConnections made from inside the app still do not trust these certificates, leading to the errors and home-grown override described above.
The problem you are facing is that JAX-RS does not use the default SSLContext and HostnameVerifier. This is actually a good thing as you can programmatically set these for the client connection rather than having it use the global. In order to do this, you need to create a ClientBuilder and set the hostname verifier and sslcontext to something you control.
public Client buildUnsecureRestClient() throws GeneralSecurityException {
SSLContext context = SSLContext.getInstance("TLSv1");
TrustManager[] trustManagerArray = {
...
};
context.init(myKeyManagers, trustManagerArray, new SecureRandom());
return ClientBuilder.newBuilder()
.hostnameVerifier(myhostnameverifier)
.sslContext(context)
.build();
}
By setting the SSLContext to contain your own key manager which you can build programmatically. I have a sample where I used the techique to bypass the SSL certificate checks for JAX-RS, but you can implement the X509KeyManager to provide your keys. The X509TrustManager is a bit tricker to deal with, and I would recommend you use the one that comes with your target JDK even if it will lock you to Oracle's which is sun.security.ssl.X509TrustManagerImpl, but since this is WebStart chances are the JRE you are using is from Oracle rather than IBM JREs.
I need to implement the Apache HttpClient SecureProtocolSocketFactory interface to create SSL sockets that do not use the SSLv2Hello protocol and make SSLv3 handshakes. From the documentation, the process to modify the protocol list is to call setEnabledProtocols on the SSLSocket. I believe I need to do this after creating it but before connecting it because connection initiates the SSL handshake and it's the SSL handshake protocol I'm trying to change.
This is mostly fine: rather than using the with-parameter context.getSocketFactory().createSocket(...) overloads which both create and connect the sockets then I can use the parameterless overload to create the socket, set it up and then connect it myself. The problem is there's another overload of createSocket() in SSLSocketFactory that wraps an existing socket with SSL, and that initiates the handshake immediately i.e. I do not have the opportunity to reconfigure it.
So to cover that case too I think I've got to throw away SSLSocket.setEnabledProtocols and instead do one of
modify my SSLContext's set of protocols which is what SSLSocketImpl uses to configure itself. However I can't see a public interface to do this so I'd need to change this by reflection, which means assuming private members of the class and also getting access to the internal class ProtocolList.
use my SSLContext only to get an SSLEngine which I can configure, and then implement my own SSL on the sockets using this.
I'm not very happy with either of these; I'd probably lean towards reflection since Java 7+ is dropping SSLv2Hello so if the classes change in the future and my reflection breaks then that's not actually a problem. I have a new instance of SSLContext already for a custom trust store.
Have I missed something - a public mechanism on SSLContext or SSLSocketFactory to set this up? Is there a better, cleaner way to do this? Thanks!
On further investigation, I don't think reflection is possible either. I was looking at the JDK8 source since that's what I had to hand, whereas in the JDK6 source SSLSocket initialises itself from a static list, not from an SSLContext or SSLContextSpi property:
enabledProtocols = ProtocolList.getDefault();
and the source field behind getDefault is static final so I can't modify that either :-(
So I'm running out of ideas. I'm still not keen on reimplementing SSLSocket (2000+ lines of it) so I think it's back to setEnabledProtocols and hope my HTTPS client never has to negotiate up a connection to SSL.
I ran into very similar problem.
I think you can break most of the issues down to the question, why can we override enabled ciphers in a custom socket factory but we cannot set the procol versions returned by getEnabledProtocols(): String[]?
Or even more simple:
If you could set enabled ciphers and protocol versions in SSLContext, you could use any HttpsConnection / SSLClient and SSLServerSocket and even libaries (Apache HttpClient, OKHttp, ...) and be sure that your security requirements in terms of SSL protocol versions and ciphers are both met.
Unfortunately most people stop at the point where a connection is established, security is just an announce to them.
I am trying to get the Apache Commons HttpClient library (version 3.1) to ignore the fact that the server certificate cannot be established as trusted (as evidenced by the thrown exception 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 did find Make a connection to a HTTPS server from Java and ignore the validity of the security certificate as well as Disable Certificate Validation in Java SSL Connections, but the accepted answer to the first is for HttpClient 4.0 (unfortunately I cannot upgrade, unless someone can point me in the direction of how to use two different versions of that same library within the same project), although it does have another answer with little more than a dead link that supposedly went to a 3.x solution. The code at the second page seems to have no effect at all when I use a slightly adjusted version of it (basically, declaring the classes in the old fashion rather than using anonymous ones inline, as well as applying to TLS in addition to SSL, using SSL for the default HTTPS socket factory as done in the example code).
Preferably, I'd like something that is thread-/instance-wide, so that any HttpClient instance (and/or related classes) being created from within my servlet code (not another servlet running in the same container) will use the lax certificate validation logic, but at this point I am starting to feel like anything will do as long as it accepts the self-signed certificate as valid.
Yes, I am aware that there are security implications, but the only reason why I need this at all is for testing purposes. The idea is to implement a configuration option that controls whether normally untrusted certificates are trusted or not, and leave it in "don't trust non-trustworthy server certificates" as default. That way it can easily be turned on or off in development, but doing it in production will require going out of one's way.
To accept selfsigned certificates we use the following code for a particular HttpConnection from commons http client.
HttpConnection con = new HttpConnection(host, port);
con.setProtocol(new Protocol("easyhttps", (ProtocolSocketFactory)new EasySSLProtocolSocketFactory(), port));
The EasySSLProtocolSocketFactory can be found in the contrib ssl package. And this can be used to make only single connections with the reduced security setting.
It seems like this can also be used to set the protocol for every client as shown here:
Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)new EasySSLProtocolSocketFactory(), 443);
Protocol.registerProtocol("https", easyhttps);
HttpClient client = new HttpClient();
GetMethod httpget = new GetMethod("https://localhost/");
client.executeMethod(httpget);
But I think that will also influence connections from other servlets.
[Edit]
Sorry, I don't know if this will work for you. Just recognized that we are using client 3.0.1 and not 3.1.