杰瑞科技汇

Java如何处理HTTPS的WSDL服务?

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:

  1. Invoking a Secure Web Service (HTTPS) from Java Client: The most common task.
  2. Generating Java Code from a WSDL (with HTTPS): The first step before you can invoke a service.
  3. 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.com but you're calling api.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 .java files.
  • -verbose: Shows detailed output.
  • The -J-... flags pass JVM options to the wsimport tool.

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:

  1. Obtaining a Keystore: A file containing your server's private key and its public certificate (e.g., a .jks or .p12 file). You can create a self-signed one for development or get one from a CA for production.
  2. 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.
分享:
扫描分享到社交APP
上一篇
下一篇