System.setProperty("javax.net.ssl.keyStoreType", "pkcs12"); System.setProperty("javax.net.ssl.keyStore", "keystore.p12"); System.setProperty("javax.net.ssl.keyStorePassword", "1234"); System.setProperty("javax.net.ssl.trustStore", "cacerts");
But it is not flexible enough if the application has gateway structure and needs different SSL configuration for each different channels. So we need a workaround.
Below example is based on the client stub that generated with Apache CXF.(Recommanded)
Personally I prefer to use Apache CXF to generate client stub instead of Apache Axis. It is more flexible and more user friendly.
Maven Config to generate client stub:
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>3.0.4</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <sourceRoot>${project.basedir}/src/main/java</sourceRoot> <wsdlOptions> <wsdlOption> <wsdl>${project.basedir}/src/main/resources/schemas/sample-ws.wsdl</wsdl> <bindingFiles> <bindingFile>${project.basedir}/src/main/resources/schemas/bindings/binding.xjb</bindingFile> </bindingFiles> <extraargs> <extraarg>-impl</extraarg> <extraarg>-client</extraarg> <extraarg>-verbose</extraarg> <extraarg>-p</extraarg> <extraarg>com.sample.ws</extraarg> <extraarg>-xjc-Xvalue-constructor</extraarg> </extraargs> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.jvnet.jaxb2_commons</groupId> <artifactId>jaxb2-value-constructor</artifactId> <version>3.0</version> </dependency> <dependency> <groupId>org.jvnet.jaxb2_commons</groupId> <artifactId>jaxb2-basics</artifactId> <version>0.6.4</version> </dependency> </dependencies> </plugin>
After the client stub is generated, if we want to make web service call, the client code is like below:
public SampleResponse authenticate(SampleRequest request) { SampleServiceInterface port = this.getPort(); return port.authenticate(request); } public SampleServiceInterface getPort() { SampleServiceInterfaceService ss = new SampleServiceInterfaceService(); SampleServiceInterface port = ss.getSampleServiceAPI(); return port; }
To customize the SSL configuration, we need to write the customized SSLSocketFactory. To implement this, we can create one SSLSocketFactoryGenerator, like below:
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import org.apache.commons.lang.StringUtils; public class SSLSocketFactoryGenerator { private byte[] keyStore = null; private byte[] trustStore = null; private String alias = null; private String keyStoreType = null; private String keyStorePassword = null; private String trustStoreType = null; private String trustStorePassword = null; public SSLSocketFactoryGenerator(ClientConfig config) { this.alias = config.getKeyAlias(); this.keyStore = config.getKeyStoreBytes(); this.trustStore = config.getTrustStoreBytes(); this.keyStoreType = config.getKeyStoreType(); this.keyStorePassword = config.getKeystorePassword(); this.trustStoreType = config.getTrustStoreType(); this.trustStorePassword = config.getTruststorePassword(); } public SSLSocketFactory getSSLSocketFactory() throws IOException, GeneralSecurityException { KeyManager[] keyManagers = getKeyManagers(); TrustManager[] trustManagers = getTrustManagers(); SSLContext context = SSLContext.getInstance("SSL"); context.init(keyManagers, trustManagers, null); SSLSocketFactory ssf = context.getSocketFactory(); return ssf; } public String getAlias() { return alias; } public void setAlias(String alias) { this.alias = alias; } public byte[] getKeyStore() { return keyStore; } public void setKeyStore(byte[] keyStore) { this.keyStore = keyStore; } public byte[] getTrustStore() { return trustStore; } public void setTrustStore(byte[] trustStore) { this.trustStore = trustStore; } public String getKeyStoreType() { return keyStoreType; } public void setKeyStoreType(String keyStoreType) { this.keyStoreType = keyStoreType; } public String getKeyStorePassword() { return keyStorePassword; } public void setKeyStorePassword(String keyStorePassword) { this.keyStorePassword = keyStorePassword; } public String getTrustStoreType() { return trustStoreType; } public void setTrustStoreType(String trustStoreType) { this.trustStoreType = trustStoreType; } public String getTrustStorePassword() { return trustStorePassword; } public void setTrustStorePassword(String trustStorePassword) { this.trustStorePassword = trustStorePassword; } private KeyManager[] getKeyManagers() throws IOException, GeneralSecurityException { String alg = KeyManagerFactory.getDefaultAlgorithm(); KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg); InputStream fis = new ByteArrayInputStream(getKeyStore()); KeyStore ks = KeyStore.getInstance(getKeyStoreType()); ks.load(fis, getKeyStorePassword().toCharArray()); fis.close(); kmFact.init(ks, StringUtils.isEmpty(getKeyStorePassword())?null:getKeyStorePassword().toCharArray()); KeyManager[] kms = kmFact.getKeyManagers(); return kms; } protected TrustManager[] getTrustManagers() throws IOException, GeneralSecurityException { String alg = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmFact = TrustManagerFactory.getInstance(alg); InputStream fis = new ByteArrayInputStream(getTrustStore()); KeyStore ks = KeyStore.getInstance(getTrustStoreType()); ks.load(fis, StringUtils.isEmpty(getTrustStorePassword())?null:getTrustStorePassword().toCharArray()); fis.close(); tmFact.init(ks); TrustManager[] tms = tmFact.getTrustManagers(); return tms; } }
This is a quite basic sample, for different scenarios, it should be quite easy to make complementary or revision. In above sample, we can create customized SSLSocketFactory by passing in different configuration data(ClientConfig.java).
In this case, we also need to revise the previous getPort function to use this customized SSLSocketFactory.
In this case, for each different channels that need different SSL configuration. We can create different service.import java.io.IOException; import java.security.GeneralSecurityException; import java.util.Properties; import javax.annotation.PostConstruct; import javax.naming.ConfigurationException; import javax.xml.ws.BindingProvider; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; public class SampleServiceImpl implements SampleService { private static final Logger logger = Logger.getLogger(SampleServiceImpl.class); private static final String URI = "/auth"; protected ClientConfig clientConfig; public SampleServiceImpl() throws ConfigurationException, IOException { super(); } void loadDefaultIfEmptyClientConfig() throws IOException, ConfigurationException { if (this.getClientConfig() != null) return; Properties properties = new Properties(); properties.load(this.getClass().getClassLoader().getResourceAsStream("config/sample.properties")); setClientConfig(new ClientConfig(properties)); } public SampleServiceImpl(Properties properties) throws ConfigurationException, IOException { super(); this.setClientConfig(new ClientConfig(properties)); } public SampleServiceImpl(ClientConfig clientConfig) throws ConfigurationException { super(); this.setClientConfig(clientConfig); } public SampleServiceInterface getPort(String relUrl) { SampleServiceInterfaceService ss = new SampleServiceInterfaceService(); SampleServiceInterface port = ss.getSampleServiceAPI(); BindingProvider bindingProvider = (BindingProvider) port; try { bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", new SSLSocketFactoryGenerator(clientConfig).getSSLSocketFactory()); if(StringUtils.isNotEmpty(relUrl)){ bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, String.format("%s%s", this.clientConfig.getServiceUrl(), relUrl)); } } catch (IOException e) { logger.error("Error happened when initializing SSLFactory.", e); throw new IllegalArgumentException(e); } catch (GeneralSecurityException e) { logger.error("Error happened when initializing SSLFactory.", e); throw new IllegalArgumentException(e); } return port; } public ClientConfig getClientConfig() { return clientConfig; } public void setClientConfig(ClientConfig clientConfig) throws ConfigurationException { this.clientConfig = clientConfig; } @Override public SampleResponse authenticate(SampleRequest request) { SampleServiceInterface port = this.getPort(); return port.authenticate(request); } }