We have been looking for a soution to our problem for one week and after looking in a lot of similar posts and after reading all the available documentation we haven't got a solution.
I'm going to explain my current situation and I hope someone could help us to solve the problem.
We have developed a Web Service Client in JAX-WS, which comunicates with a server-side Web Service in another platform. The comunication is 2 way SSL and we have the CA of the server side, to trust him, and our private certificates to identify in the server side.
Our Web Service Client is deployed in a Weblogic 10.3 and when it is going to make a call to the server-side Web Service, we load dinamically the truststore and the keystore which is loaded with just one certificate, because each time we are going to use a different certificate so we cannot use only a static keystore.
The problem is when we are stablishing the connection, negotiating the handshake because Weblogic ignores the truststore and keystore we loaded before the call and only look for the trusted certificates and the private keys in the Weblogic's keystores...
If we put our trusted certificate in the truststore of Weblogic and start the comunication again. We begin the handshake, we trust the server side (because now Weblogic find the CA in its keystore), but when our Web Service Client has to send the certificate to be trusted by the server-side, the "Certificate Chain" is empty and we get a "BAD_CERTIFICATE".
We have tried with Apache CXF and JAX-WS and the problem is the same, setting the keystores using the System properties and the libraries... So we don't know why our Web Service Client is not capable to send the certificate. It seems Weblogic is not serving them because of some reason, maybe Weblogic configuration, but we don't know.
If someone knows what we can do, plase tell us.
Thanks in advance.
Your problem is the SSL configuration on your client and server side. SSL handshakes issues aren't related to your web services configuration: Web services goes over HTTP, and is the java HttpsURLConnection
that handle this.
A 2-way SSL handshake requires:
Client side
- The server certificate should be in the keystore, or you can turn of this validation as explained setting this before using your web service client:
Code:
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
} };
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
}
- The server certificate 'server name' must match with the URL you are using to access the web service endpoint (
checkURLSpoofing
) or turn off this validation setting this before using your service client
Code:
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
- Configure the client-side to sent correctly its certificate. Set this before using your web service client
Code:
System.setProperty("javax.net.ssl.keyStore", "path/to/your/key");
System.setProperty("javax.net.ssl.keyStorePassword", "your-keystore-password");
System.setProperty("javax.net.ssl.keyStoreType", "JKS");
System.setProperty("javax.net.ssl.trustStore", "path/to/your/trust/keystore");
System.setProperty("javax.net.ssl.trustStorePassword", "your-truststore-password");
System.setProperty("com.sun.net.ssl.dhKeyExchangeFix", "true");
Server side
- Add the client certificate to the trust store used by your server
This should work, if you keep having issues, add the stacktrace (client and server) to see what is going on.
Step by step instruction.
See SSL configuration for WebLogic Server, Configuring Two-Way SSL for a Client Application and Configure two-way SSL.
keytool -genkey -alias server-alias -keyalg RSA -keypass changeit_0 -storepass changeit_1 -keystore server_keystore.jks -validity 1825
keytool -export -alias server-alias -storepass changeit_1 -file server.cer -keystore server_keystore.jks
keytool -genkey -alias client-alias -keyalg RSA -keypass changeit_2 -storepass changeit_3 -keystore client_keystore.jks -validity 1825
keytool -export -alias client-alias -storepass changeit_3 -file client.cer -keystore client_keystore.jks
#WLHOME\server\lib\cacerts
keytool -import -v -trustcacerts -alias client-alias -file client.cer -keystore cacerts -keypass changeit_2 -storepass changeit
Server->Keystores
Keystores: Custom Identity and Custom Trust
Custom Identity Keystore: server_keystore.jks
Custom Identity Keystore Type: JKS
Custom Identity Keystore Passphrase: changeit_1
Custom Trust Keystore: <WLHOME>\server\lib\cacerts
Custom Trust Keystore Type: JKS
Custom Trust Keystore Passphrase: changeit
Server->General
SSL Listen Port Enabled: true
Server->SSL
Private Key Alias: server-alias
Private Key Passphrase: changeit_0
Hostname Verification: None
Use Server Certs: true
Two Way Client Cert Behavior: Client Certs Requested and Enforced
SSLRejection Logging Enabled: true
1) Service header.
@WebService
@Policy(uri = "policy:Wssp1.2-2007-Https-ClientCertReq.xml")
2) Client HandlerResolver.
package org.ru5.test;
import java.util.*;
import javax.xml.namespace.QName;
import javax.xml.ws.*;
public class MustUnderstandHeadersHandler implements HandlerResolver {
@Override
public List<Handler> getHandlerChain(PortInfo portInfo) {
final List<Handler> handlerList = new ArrayList<Handler>(1);
handlerList.add(new MustUnderstandHeadersSOAPHandler());
return handlerList;
}
private class MustUnderstandHeadersSOAPHandler implements SOAPHandler<SOAPMessageContext> {
private static final String LOCAL_PART = "Security";
private static final String PREFIX = "wsse";
private static final String URI =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
@Override
public Set<QName> getHeaders() {
final QName securityHeader = new QName(URI, LOCAL_PART, PREFIX);
final Set<QName> headers = new HashSet<QName>();
headers.add(securityHeader);
return headers;
}
@Override
public boolean handleMessage(SOAPMessageContext context) {
return true;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
@Override
public void close(javax.xml.ws.handler.MessageContext context) {
}
}
}
I got many problems with SSL and JAX-WS until I changed a setting in Weblogic : use JSSE.
Depending on the version of weblogic (10.3.x, the x is changing everything) this may or may not be possible.
It worked for me on 10.3.3 and 10.3.5, which are all Weblogic 11g.
This change can be done using the console (in security, advanced) or command line -Dweblogic.security.SSL.enableJSSE=true
The reason is that in previous oracle implementation of SSL (if not using JSSE) some certificates are not accepted depending on algorithm and size of key. Plus this implementation uses different settings and will not use JSSE settings for settings like javax.net.ssl.keyStore...
I am now using this JSSE + -Djavax.net.debug=ssl:verbose and everything is clear.