CXF代码配置实现HTTPS

本文主要简单讲述 CXF 配置 HTTPS 及所需 Keystore 的两种方式:XML 和代码配置,不会着重于介绍 CXF 本身。

完整代码:github待上传
1. HTTPS HTTPS 可以简单理解为 HTTP+TLS/SSL 是一种保证网络请求安全的应用层协议
本文主要介绍双向验证的实现方式,下一篇文章介绍单项验证的实现
HTTPS双向认证(Mutual TLS authentication) https://help.aliyun.com/docum...
从所需条件上说双向验证需要Server端和Client端都有公钥与私钥
2. XML 配置 CXF 支持HTTPS 2.1 Keystore
keystore是一种存放公钥与私钥的加密文件,其中JKS格式为Java自带工具(keytool)生成,双向验证需要以下keystore
  • Server keystore: 包含服务器本身的私钥,以及所信任的Client端的公钥(CA证书注入得到)
  • Client keystore: 包含client端本身的私钥,以及所信任的Server端的公钥
2.1.1 生成命令
# 目录:resources/https_mutual_TLS_auth/keystore # 服务器部分 # 生成服务端私钥,注意jdk11默认生成PKCS12的内部格式,需要用storetype指定jks格式,且JKS格式可以指定keystore的密码和内部key的密码,PKCS12的这两个密码默认相等 keytool -genkeypair -alias myservicekey -keystore serviceKeystore.jks -storetype jks -dname "cn=localhost" -keypass keypass -storepass storepass # 将服务器私钥导出为证书(公钥) keytool -export -rfc -keystore serviceKeystore.jks -alias myservicekey -file MyService.cer -storepass storepass# 生成client的私钥 keytool -genkeypair -alias myclientkey -keystore clientKeystore.jks -storetype jks -keypass keypass -storepass storepass # 导出证书 keytool -export -rfc -keystore clientKeystore.jks -alias myclientkey -file MyClient.cer -storepass storepass# 注入:将服务器的证书作为信任的公钥导入client的keystore keytool -import -noprompt -trustcacerts -file MyService.cer -alias myservicekey -keystore clientKeystore.jks -storetype jks -storepass storepass # 同理 client的公钥导入服务器的keystore keytool -import -noprompt -trustcacerts -file MyClient.cer -alias myclientkey -keystore serviceKeystore.jks -storetype jks -storepass storepass

2.1.2 内部构成
# clientKeystore keytool -list -keystore clientKeystore.jks -storepass storepass 密钥库类型: JKS 密钥库提供方: SUN您的密钥库包含 2 个条目myclientkey, 2022年3月31日, PrivateKeyEntry, 证书指纹 (SHA-256): F5:86:67:3E:4A:D8:3C:82:37:63:D6:00:D6:FF:F0:96:1E:43:08:E1:E6:81:02:3E:1F:9C:3E:E5:7A:24:DA:AF myservicekey, 2022年3月31日, trustedCertEntry, 证书指纹 (SHA-256): B0:EE:DD:89:EC:93:B2:B0:2C:4F:E5:97:27:D2:3D:46:CD:FF:BC:B6:CD:19:04:6D:20:1C:63:E1:C3:1A:2C:41# serviceKeystore keytool -list -keystore serviceKeystore.jks -storepass storepass 密钥库类型: JKS 密钥库提供方: SUN您的密钥库包含 2 个条目myclientkey, 2022年3月31日, trustedCertEntry, 证书指纹 (SHA-256): F5:86:67:3E:4A:D8:3C:82:37:63:D6:00:D6:FF:F0:96:1E:43:08:E1:E6:81:02:3E:1F:9C:3E:E5:7A:24:DA:AF myservicekey, 2022年3月31日, PrivateKeyEntry, 证书指纹 (SHA-256): B0:EE:DD:89:EC:93:B2:B0:2C:4F:E5:97:27:D2:3D:46:CD:FF:BC:B6:CD:19:04:6D:20:1C:63:E1:C3:1A:2C:41Warning: JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore serviceKeystore.jks -destkeystore serviceKeystore.jks -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。

2.2 配置文件
【CXF代码配置实现HTTPS】与之后的代码配置部分相对应
# resources/https_mutual_TLS_auth/ServerConfig.xml

# resources/https_mutual_TLS_auth/SecureClient.xml

2.3 使用
2.3.1 POM 注意:plugin是为了保证keystore不会被编译,否则内部加密二进制会错掉
11 11 3.5.1 org.springframework spring-beans 4.3.30.RELEASE org.springframework spring-core 4.3.30.RELEASE org.springframework spring-context 4.3.30.RELEASE com.sun.xml.ws jaxws-rt 2.2.3 stax-ex org.jvnet.staxex mimepull org.jvnet wstx-asl org.codehaus.woodstox org.jvnet.jax-ws-commons.spring jaxws-spring 1.8 junit junit 4.13.2 test org.apache.cxf cxf-rt-frontend-jaxws ${cxfVersion} org.apache.cxf cxf-rt-transports-http ${cxfVersion} org.apache.cxf cxf-rt-databinding-aegis ${cxfVersion} org.apache.cxf cxf-rt-transports-http-jetty ${cxfVersion} saaj-impl com.sun.xml.messaging.saaj org.apache.maven.plugins maven-resources-plugin 2.6 jks

2.3.2 模拟业务
// User类随便写个就行 @WebService public interface HaloServer {String sayHi(@WebParam(name="text")String text); String sayHiToUser(User user); }@WebService(endpointInterface = "com.pal.server.HaloServer",serviceName = "HaloServer") public class HaloServerImpl implements HaloServer {@Override public String sayHi(String text) { return "Hi " + text; }@Override public String sayHiToUser(User user) { return "Halo "+user.getName(); } }

2.3.3 正式使用
/** * 双向验证测试 */ public class HttpsMutualTLSAuthTest {int port = 9001; HaloServer endpoint = new HaloServerImpl(); @Test public void xmlConfigTest() { // 防止出现双象验证协议版本不一致 System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3"); // XML配置版本// 创建配置工厂Bean JaxWsServerFactoryBean serverFactoryBean = new JaxWsServerFactoryBean(); // 创建读取配置文件的bus URL serverConfigFile = HttpsDemo.class.getResource("/https_mutual_TLS_auth/ServerConfig.xml"); Bus serverConfigBus = new SpringBusFactory().createBus(serverConfigFile.toString()); // 设置参数及配置 serverFactoryBean.setBus(serverConfigBus); serverFactoryBean.setServiceClass(HaloServer.class); serverFactoryBean.setAddress("https://localhost:"+port+"/halo"); serverFactoryBean.setServiceBean(endpoint); // 启动Server Server server = serverFactoryBean.create(); //Client URL clientConfigFile = HttpsDemo.class.getResource("/https_mutual_TLS_auth/SecureClient.xml"); Bus clientConfigBus = new SpringBusFactory().createBus(clientConfigFile.toString()); JaxWsProxyFactoryBean clientFactoryBean = new JaxWsProxyFactoryBean(); clientFactoryBean.setBus(clientConfigBus); clientFactoryBean.setServiceClass(HaloServer.class); clientFactoryBean.setAddress("https://localhost:"+port+"/halo"); HaloServer client = (HaloServer) clientFactoryBean.create(); // 调用 System.out.println(client.sayHi("world")); User user = new User("Tim", 202); System.out.println(client.sayHiToUser(user)); // 关闭 server.stop(); System.out.println("Server stop"); }}

3. 代码配置方式 注:前边的keystore文件和业务模拟类都通用
3.1 实现代码
/** * 双向验证测试 */ public class HttpsMutualTLSAuthTest {int port = 9001; HaloServer endpoint = new HaloServerImpl(); @Test public void test() throws GeneralSecurityException, IOException {// 防止出现双象验证协议版本不一致 System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3"); // 创建配置工厂Bean JaxWsServerFactoryBean serverFactoryBean = new JaxWsServerFactoryBean(); // 创建配置的bus Bus serverConfigBus = new SpringBusFactory().createBus(); serverConfigBus.setExtension(getServerEngineFactory(),JettyHTTPServerEngineFactory.class); // 设置参数及配置 serverFactoryBean.setBus(serverConfigBus); serverFactoryBean.setServiceClass(HaloServer.class); serverFactoryBean.setAddress("https://localhost:"+port+"/halo"); serverFactoryBean.setServiceBean(endpoint); // 启动Server Server server = serverFactoryBean.create(); //Client URL clientConfigFile = HttpsDemo.class.getResource("/https_mutual_TLS_auth/SecureClient.xml"); Bus clientConfigBus = new SpringBusFactory().createBus(); JaxWsProxyFactoryBean clientFactoryBean = new JaxWsProxyFactoryBean(); clientFactoryBean.setBus(clientConfigBus); clientFactoryBean.setServiceClass(HaloServer.class); clientFactoryBean.setAddress("https://localhost:"+port+"/halo"); HaloServer client = (HaloServer) clientFactoryBean.create(); clientConfigBus.setExtension(getClientHttpConduit(client),HTTPConduit.class); // 调用 System.out.println(client.sayHi("world")); User user = new User("Tim", 202); System.out.println(client.sayHiToUser(user)); // 关闭 server.stop(); System.out.println("Server stop"); }/** * 获取Server端的EngineFactory用来装进Bus然后设置到JaxWsServerFactoryBean * @return JettyHTTPServerEngineFactory 对应 ServerConfig.xml 中的标签 * @throws GeneralSecurityException * @throws IOException */ public JettyHTTPServerEngineFactory getServerEngineFactory() throws GeneralSecurityException, IOException {// 对应 XML 中 TLSServerParameters tlsSp = new TLSServerParameters(); ClientAuthentication clientAuthentication = new ClientAuthentication(); clientAuthentication.setRequired(true); clientAuthentication.setWant(true); tlsSp.setClientAuthentication(clientAuthentication); URL serverResourceUrl = HttpsMutualTLSAuthTest.class.getResource("/https_mutual_TLS_auth/keystore/serviceKeystore.jks"); FileInputStream inputStream = new FileInputStream(serverResourceUrl.getPath()); // KeyStore serverKeystore = KeyStore.getInstance("JKS"); serverKeystore.load(inputStream,"storepass".toCharArray()); // 对应及其中内容 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(serverKeystore,"keypass".toCharArray()); tlsSp.setKeyManagers(kmf.getKeyManagers()); // 对应 TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(serverKeystore); tlsSp.setTrustManagers(tmf.getTrustManagers()); // 注意这里的 jettyHTTPServerEngine 生成方式有很多,详情看源码 JettyHTTPServerEngineFactory jettyHTTPServerEngineFactory = new JettyHTTPServerEngineFactory(); jettyHTTPServerEngineFactory.setTLSServerParametersForPort(port,tlsSp); return jettyHTTPServerEngineFactory; }/** * 获取客户端的HTTPConduit用来装进Bus然后设置到JaxWsProxyFactoryBean * * @param haloServer 服务对象的代理对象 * @return HTTPConduit 对应 SecureClient.xml 中的标签 * @throws GeneralSecurityException * @throws IOException */ public HTTPConduit getClientHttpConduit(HaloServer haloServer) throws GeneralSecurityException, IOException {// TLSClientParameters tlsCp = new TLSClientParameters(); tlsCp.setDisableCNCheck(false); URL clientResourceUrl = HttpsMutualTLSAuthTest.class.getResource("/https_mutual_TLS_auth/keystore/clientKeystore.jks"); FileInputStream inputStream = new FileInputStream(clientResourceUrl.getPath()); // KeyStore clientKeystore = KeyStore.getInstance("JKS"); clientKeystore.load(inputStream,"storepass".toCharArray()); // KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(clientKeystore,"keypass".toCharArray()); tlsCp.setKeyManagers(kmf.getKeyManagers()); // TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(clientKeystore); tlsCp.setTrustManagers(tmf.getTrustManagers()); HTTPConduit conduit = (HTTPConduit)ClientProxy.getClient(haloServer).getConduit(); conduit.setTlsClientParameters(tlsCp); return conduit; } }

    推荐阅读