共计 13303 个字符,预计需要花费 34 分钟才能阅读完成。
本文次要简略讲述 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:41
Warning:
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 类轻易写个就行
@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 中的 <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;
}
}
正文完