本文次要简略讲述 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的keystorekeytool -import -noprompt -trustcacerts -file MyService.cer -alias myservicekey -keystore clientKeystore.jks -storetype jks -storepass storepass# 同理 client的公钥导入服务器的keystorekeytool -import -noprompt -trustcacerts -file MyClient.cer -alias myclientkey -keystore serviceKeystore.jks -storetype jks -storepass storepass

2.1.2 外部形成

# clientKeystorekeytool -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:AFmyservicekey, 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# serviceKeystorekeytool -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:AFmyservicekey, 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 配置文件

与之后的代码配置局部绝对应

# resources/https_mutual_TLS_auth/ServerConfig.xml<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:sec="http://cxf.apache.org/configuration/security"    xmlns:http="http://cxf.apache.org/transports/http/configuration"    xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"    xsi:schemaLocation="http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://cxf.apache.org/transports/http-jetty/configuration http://cxf.apache.org/schemas/configuration/http-jetty.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <http:destination name="*.http-destination">    </http:destination>    <httpj:engine-factory>        <httpj:engine port="9001">            <httpj:tlsServerParameters>                <sec:keyManagers keyPassword="keypass">                    <sec:keyStore file="src/main/resources/https_mutual_TLS_auth/keystore/serviceKeystore.jks" password="storepass" type="JKS"/>                </sec:keyManagers>                <sec:trustManagers>                    <sec:keyStore file="src/main/resources/https_mutual_TLS_auth/keystore/serviceKeystore.jks" password="storepass" type="JKS"/>                </sec:trustManagers>                <sec:clientAuthentication want="true" required="true"/>            </httpj:tlsServerParameters>        </httpj:engine>    </httpj:engine-factory></beans>
# resources/https_mutual_TLS_auth/SecureClient.xml<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:sec="http://cxf.apache.org/configuration/security"    xmlns:http="http://cxf.apache.org/transports/http/configuration"    xsi:schemaLocation="http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <http:conduit name="*.http-conduit">        <http:tlsClientParameters disableCNCheck="true">            <sec:keyManagers keyPassword="keypass">                <sec:keyStore file="src/main/resources/https_mutual_TLS_auth/keystore/clientKeystore.jks" password="storepass" type="JKS"/>            </sec:keyManagers>            <sec:trustManagers>                <sec:keyStore file="src/main/resources/https_mutual_TLS_auth/keystore/clientKeystore.jks" password="storepass" type="JKS"/>            </sec:trustManagers>        </http:tlsClientParameters>    </http:conduit></beans>

2.3 应用

2.3.1 POM

留神:plugin是为了保障keystore不会被编译,否则外部加密二进制会错掉

<properties>        <maven.compiler.source>11</maven.compiler.source>        <maven.compiler.target>11</maven.compiler.target>        <cxfVersion>3.5.1</cxfVersion>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-beans</artifactId>            <version>4.3.30.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-core</artifactId>            <version>4.3.30.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>4.3.30.RELEASE</version>        </dependency>        <dependency>            <groupId>com.sun.xml.ws</groupId>            <artifactId>jaxws-rt</artifactId>            <version>2.2.3</version>            <exclusions>                <exclusion>                    <artifactId>stax-ex</artifactId>                    <groupId>org.jvnet.staxex</groupId>                </exclusion>                <exclusion>                    <artifactId>mimepull</artifactId>                    <groupId>org.jvnet</groupId>                </exclusion>                <exclusion>                    <artifactId>wstx-asl</artifactId>                    <groupId>org.codehaus.woodstox</groupId>                </exclusion>            </exclusions>        </dependency>        <dependency>            <groupId>org.jvnet.jax-ws-commons.spring</groupId>            <artifactId>jaxws-spring</artifactId>            <version>1.8</version>        </dependency>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>4.13.2</version>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.apache.cxf</groupId>            <artifactId>cxf-rt-frontend-jaxws</artifactId>            <version>${cxfVersion}</version>        </dependency>        <dependency>            <groupId>org.apache.cxf</groupId>            <artifactId>cxf-rt-transports-http</artifactId>            <version>${cxfVersion}</version>        </dependency>        <dependency>            <groupId>org.apache.cxf</groupId>            <artifactId>cxf-rt-databinding-aegis</artifactId>            <version>${cxfVersion}</version>        </dependency>        <dependency>            <groupId>org.apache.cxf</groupId>            <artifactId>cxf-rt-transports-http-jetty</artifactId>            <version>${cxfVersion}</version>            <exclusions>                <exclusion>                    <artifactId>saaj-impl</artifactId>                    <groupId>com.sun.xml.messaging.saaj</groupId>                </exclusion>            </exclusions>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-resources-plugin</artifactId>                <version>2.6</version>                <configuration>                    <nonFilteredFileExtensions>                        <nonFilteredFileExtension>jks</nonFilteredFileExtension>                    </nonFilteredFileExtensions>                </configuration>            </plugin>        </plugins>    </build>

2.3.2 模仿业务

// User类轻易写个就行@WebServicepublic 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 中的 <httpj:engine-factory> 标签     * @throws GeneralSecurityException     * @throws IOException     */    public JettyHTTPServerEngineFactory getServerEngineFactory() throws GeneralSecurityException, IOException {                // 对应 XML 中 <httpj:tlsServerParameters>        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());                // <sec:keyStore>        KeyStore serverKeystore = KeyStore.getInstance("JKS");        serverKeystore.load(inputStream,"storepass".toCharArray());                // 对应 <sec:keyManagers keyPassword="keypass"> 及其中内容        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());        kmf.init(serverKeystore,"keypass".toCharArray());        tlsSp.setKeyManagers(kmf.getKeyManagers());        // 对应 <sec:trustManagers>        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 中的 <http:conduit name="*.http-conduit"> 标签     * @throws GeneralSecurityException     * @throws IOException     */    public HTTPConduit getClientHttpConduit(HaloServer haloServer) throws GeneralSecurityException, IOException {                // <http:tlsClientParameters disableCNCheck="true">        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());        // <sec:keyStore>        KeyStore clientKeystore = KeyStore.getInstance("JKS");        clientKeystore.load(inputStream,"storepass".toCharArray());        // <sec:keyManagers keyPassword="keypass">        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());        kmf.init(clientKeystore,"keypass".toCharArray());        tlsCp.setKeyManagers(kmf.getKeyManagers());        // <sec:trustManagers>        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;    }}