Of course! Let's break down how to work with HTTPS and WSDL in Java. This is a very common requirement for enterprise applications that need to consume secure web services.
We'll cover three main scenarios:
- Invoking a Secure Web Service (HTTPS) from Java Client: The most common task.
- Generating Java Code from a WSDL (with HTTPS): The first step before you can invoke a service.
- Publishing a Secure Web Service (HTTPS) from a Java Server: For creating your own services.
Invoking a Secure Web Service (HTTPS) from a Java Client
When you call a web service over HTTPS, your Java application needs to trust the server's SSL certificate. This is where most problems arise. The issues can be:
- The server uses a self-signed certificate: Not issued by a trusted Certificate Authority (CA). Java's default truststore doesn't have it.
- The server's certificate is issued by a private/internal CA: The CA's root certificate is not in Java's default truststore.
- The hostname in the certificate doesn't match the service URL: The certificate is for
api.mycompany.combut you're callingapi.prod.mycompany.com.
Here are the solutions, from most recommended to least recommended.
Scenario A: The Server Has a Valid, Publicly Trusted Certificate (e.g., Let's Encrypt, DigiCert)
This is the ideal case. If the certificate is issued by a CA that is already in Java's default cacerts truststore, you don't need to do anything special. Your code will work out of the box.
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import java.net.URL;
public class SecureClient {
public static void main(String[] args) throws Exception {
// The WSDL URL is now using the 'https' protocol
URL wsdlUrl = new URL("https://api.example.com/service?wsdl");
// The namespace and service name from the WSDL
QName qname = new QName("http://example.com/", "MyService");
Service service = Service.create(wsdlUrl, qname);
// Get the port (the interface for the web service)
MyServicePortType port = service.getPort(MyServicePortType.class);
// Now you can call the web service methods
String result = port.someMethod("Hello from Java");
System.out.println("Result: " + result);
}
}
Scenario B: The Server Uses a Self-Signed or Internal CA Certificate
You need to tell Java to trust the server's certificate. The best way to do this is by adding the server's CA certificate to your Java truststore.
Step 1: Get the Server's Certificate
Use openssl to download the server's certificate:
openssl s_client -connect api.example.com:443 -showcerts </dev/null | openssl x509 -outform PEM > server-cert.pem
Step 2: Import the Certificate into Java's Truststore
Find your Java cacerts file (it's usually in the lib/security directory of your JDK/JRE).
# For example, on a Linux/macOS system $JAVA_HOME/lib/security/cacerts # On Windows %JAVA_HOME%\lib\security\cacerts
Import the certificate. The default password for cacerts is changeit.
keytool -import -alias my-server-ca -keystore $JAVA_HOME/lib/security/cacerts -file server-cert.pem
You will be prompted to trust this certificate. Type yes.
Step 3: Run Your Code
Now, your Java client will trust the server, and the code from Scenario A will work without modification.
Scenario C: The "Trust All" Workaround (Not Recommended for Production)
This is a quick fix for development or testing where you can't modify the truststore. It tells your Java client to trust any SSL certificate, even invalid ones. This is a major security risk and should never be used in production.
You create a custom TrustManager and install it.
import javax.net.ssl.*;
import java.security.cert.X509Certificate;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import java.net.URL;
public class TrustAllClient {
public static void main(String[] args) throws Exception {
// 1. Create a TrustManager that trusts all certificates
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// 2. Create an SSLContext that uses this TrustManager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
// 3. Set the default SSLContext for the JVM
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// 4. (Optional) Also disable hostname verification
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
// 5. Now, your web service call will work
URL wsdlUrl = new URL("https://api.example.com/service?wsdl");
QName qname = new QName("http://example.com/", "MyService");
Service service = Service.create(wsdlUrl, qname);
MyServicePortType port = service.getPort(MyServicePortType.class);
String result = port.someMethod("Hello from TrustAll Java");
System.out.println("Result: " + result);
}
}
Generating Java Code from a WSDL (with HTTPS)
Before you can call a web service, you need to generate Java client stubs from its WSDL file. The standard tool for this is wsimport, which is included with the JDK.
If the WSDL is located at an HTTPS URL and the server has a self-signed/internal certificate, wsimport will fail because it can't download the WSDL file.
Solution: You need to configure wsimport to trust the server's certificate, just like the client.
Step 1: Get the Server's Certificate
Use the same openssl command as before to get server-cert.pem.
Step 2: Import into a Custom Truststore (Recommended)
Instead of modifying the global cacerts, create a new truststore for this project.
# Create a new JKS truststore file keytool -genkeypair -keystore my-truststore.jks -storepass mypassword -alias myalias -keypass mypassword -keyalg RSA -dname "CN=My Client, OU=My Dept, O=My Company, L=My City, S=My State, C=US" # Import the server's certificate into your new truststore keytool -import -alias my-server-ca -keystore my-truststore.jks -file server-cert.pem -storepass mypassword
Step 3: Run wsimport with the Custom Truststore
Use the -J-Djavax.net.ssl.trustStore and -J-Djavax.net.ssl.trustStorePassword JVM arguments to point wsimport to your custom truststore.
wsimport -keep -verbose \ -J-Djavax.net.ssl.trustStore=my-truststore.jks \ -J-Djavax.net.ssl.trustStorePassword=mypassword \ https://api.example.com/service?wsdl
-keep: Keeps the generated.javafiles.-verbose: Shows detailed output.- The
-J-...flags pass JVM options to thewsimporttool.
Publishing a Secure Web Service (HTTPS) from a Java Server
If you are creating a web service endpoint that must be accessible over HTTPS, you'll need to configure your application server (like Tomcat, JBoss, or WebSphere) or your embedded server.
This is typically done by:
- Obtaining a Keystore: A file containing your server's private key and its public certificate (e.g., a
.jksor.p12file). You can create a self-signed one for development or get one from a CA for production. - Configuring the Server: Configure the server to use this keystore for SSL/TLS connections.
Example: Creating a Self-Signed Keystore
keytool -genkeypair -keystore server-keystore.jks -storepass serverpass -alias server -keypass serverpass -keyalg RSA -dname "CN=localhost, OU=My Dept, O=My Company, L=My City, S=My State, C=US"
Example: Configuring an Embedded Jetty Server for HTTPS
If you are using a framework like Spring Boot or CXF with an embedded server, you would configure the connector to use the keystore.
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.webapp.WebAppContext;
public class SecureServer {
public static void main(String[] args) throws Exception {
Server server = new Server();
SslSelectChannelConnector sslConnector = new SslSelectChannelConnector();
sslConnector.setPort(8443); // HTTPS port
sslConnector.setKeystorePath("server-keystore.jks");
sslConnector.setKeystorePassword("serverpass");
sslConnector.setKeyPassword("serverpass"); // Often same as keystore password
server.addConnector(sslConnector);
// ... rest of your server setup (e.g., deploying your JAX-WS endpoint)
server.start();
server.join();
}
}
Summary and Best Practices
| Task | Problem | Solution | Recommendation |
|---|---|---|---|
| Client: Call HTTPS WSDL | Server cert is self-signed/internal | Add server's CA cert to Java truststore. Use a custom TrustManager. |
Best for production. |
| Client: Call HTTPS WSDL | Cert is valid but hostname mismatch | Use a custom X509HostnameVerifier in your SSLContext. |
Good for production. |
| Client: Call HTTPS WSDL | Quick fix for dev/testing | Use a "Trust All" TrustManager. |
NEVER for production. |
| Generate Code from HTTPS WSDL | wsimport can't download WSDL |
Create a custom truststore and pass it to wsimport using JVM args. |
Best practice for automation and CI/CD. |
| Server: Publish HTTPS WSDL | Need to serve service over HTTPS | Configure server (Tomcat, Jetty, etc.) with a keystore containing your server's private key and cert. | Standard and necessary for any public service. |
