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:41

Warning:
JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore serviceKeystore.jks -destkeystore serviceKeystore.jks -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。

2.2 配置文件

与之后的代码配置部分相对应

# 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;
    }
}

你可能感兴趣的:(后端webservice)