基于SSL验证的Apache CXF客户端设计总结
2014-04-19 14:47
351 查看
最近在与合作方进行接口对接,合作方只提供了一个WSDL链接和一个ce.p12证书,研究了好几天终于调通了。为了与合作方对接,我自己在本地搭建了一个使用证书验证的Tomcat,在本地用自己制作的客户端证书与本地Tomcat进行SSL链接,最终终于搞清楚了使用Apache CXF框架进行SSL链接的方法。
整个过程可以分为以下几个:
认证过程
认识证书
制作证书
编写服务器端Web Service接口
配置Tomcat的SSL访问
编写客户端调用程序
客户端 ==> 服务器端 : 客户端A需要使用自己的私钥进行签名,使用服务器端B的公钥进行加密,然后将请求数据传给服务器端B,服务器端B使用服务器端B的私钥进行解密,再用客户端A的公钥进行验签。
服务器端 ==> 客户端 : 服务器端B使用服务器端B的私钥进行签名,使用客户端A的公钥进行加密,然后将响应数据传给客户端A,客户端A用客户端A的私钥进行解密,再用服务器端B的公钥进行验签。
在JAVA中制作证书可以采用JDK中提供的keytool和openssl,两者的区别在于,如果证书中没有相关的证书链那么完全可以采用keytool,否则就需要用openssl工具。
根证书 : 根证书一般是起认证作用的,也可以称做CA,有相关的CA认证机构,不过一般需要花钱取实现CA认证,在这里根证书采用的是自签的方式。
服务器端证书 : 服务器端证书由根证书签署,在服务器端配置使用。
二级证书 : 二级证书由根证书签署,在服务器端配置使用。
客户端证书 : 客户端证书由根证书签署,在客户端配置使用。
修改完Tomcat配置后,重新启动Tomcat,在浏览器中输入https://localhost:8443/cxf-deom/service/HelloWorld?WSDL,会发现无法访问了。
再次访问https://localhost:8443/cxf-deom/service/HelloWorld?WSDL,chrome会弹出对话框,要求确认证书,点击"OK",接下来会提醒你说"该证书是不被信任的",选择"Process anyway",然后就能看到WSDL页面了。
如果最终看到了WSDL就说名Tomcat配置是正确的,证书也是正确的。
以上两种方法都可以访问SSL设置的Web Service服务,然后在控制台打印输出信息。
至此,一个通过HTTPS访问Web Service的例子就搞定了。
参考 : http://liuwuhen.iteye.com/blog/1661493
整个过程可以分为以下几个:
认证过程
认识证书
制作证书
编写服务器端Web Service接口
配置Tomcat的SSL访问
编写客户端调用程序
1. 认证过程
HTTPS认证其实是基于证书认证,一般常用的是x509认证,认证过程是这样的客户端 ==> 服务器端 : 客户端A需要使用自己的私钥进行签名,使用服务器端B的公钥进行加密,然后将请求数据传给服务器端B,服务器端B使用服务器端B的私钥进行解密,再用客户端A的公钥进行验签。
服务器端 ==> 客户端 : 服务器端B使用服务器端B的私钥进行签名,使用客户端A的公钥进行加密,然后将响应数据传给客户端A,客户端A用客户端A的私钥进行解密,再用服务器端B的公钥进行验签。
在JAVA中制作证书可以采用JDK中提供的keytool和openssl,两者的区别在于,如果证书中没有相关的证书链那么完全可以采用keytool,否则就需要用openssl工具。
2,认识证书
我们要生成的证书包括以下几个:根证书 : 根证书一般是起认证作用的,也可以称做CA,有相关的CA认证机构,不过一般需要花钱取实现CA认证,在这里根证书采用的是自签的方式。
服务器端证书 : 服务器端证书由根证书签署,在服务器端配置使用。
二级证书 : 二级证书由根证书签署,在服务器端配置使用。
客户端证书 : 客户端证书由根证书签署,在客户端配置使用。
3,制作证书
3.1 制作根证书
mkdir root # 制作根证书 openssl genrsa -out root/root-key.pem 1024 # 创建证书请求 openssl req -new -out root/root-req.csr -key root/root-key.pem -subj /C=CN/ST=BeiJing/L=BeiJing/O="Xiaomi Technologies Co. Ltd."/OU="MIUI System Safety Team"/OU="MIUI System Safety Team"/CN="localhost"/emailAddress=xxxxxx@xiaomi.com # 自签署根证书 openssl x509 -req -in root/root-req.csr -out root/root-cert.pem -signkey root/root-key.pem -days 3650 # 将证书导入到JKS文件中 /usr/lib/jvm/java/jdk1.6.0_45/bin/keytool -import -v -trustcacerts -storepass xiaomisys -alias root -file root/root-cert.pem -keystore root/root-id.jks # 将证书导出成cer文件 /usr/lib/jvm/java/jdk1.6.0_45/bin/keytool -export -alias root -keystore root/root-id.jks -file root/root-id.cer -storepass xiaomisys
3.2 制作服务器端证书
cd root #建立server文件夹 mkdir server #回到root上级一目录 cd .. #创建私钥 openssl genrsa -out root/server/temip-key.pem 1024 #创建证书请求(注意cn如果是本机应该填写localhost,如果是网站则填写域名.) openssl req -new -out root/server/temip-req.csr -key root/server/temip-key.pem -subj /C=CN/ST=BeiJing/L=BeiJing/O="Xiaomi Technologies Co. Ltd."/OU="MIUI System Safety Team"/OU="MIUI System Safety Team"/CN=localhost/emailAddress=xxxxxx@xiaomi.com #签署服务器端证书 openssl x509 -req -in root/server/temip-req.csr -out root/server/temip-cert.pem -CA root/root-cert.pem -CAkey root/root-key.pem -CAcreateserial -days 3650 #将服务器端证书PKCS12格式 openssl pkcs12 -export -clcerts -in root/server/temip-cert.pem -inkey root/server/temip-key.pem -out root/server/temip-id.p12
3.3 制作二级证书
cd root #建立client文件夹 mkdir client #回到root上一目录 cd .. #创建私钥 openssl genrsa -out root/client/eomsca-key.pem 1024 #创建证书请求 openssl req -new -out root/client/eomsca-req.csr -key root/client/eomsca-key.pem -subj /C=CN/ST=BeiJing/L=BeiJing/O="Xiaomi Technologies Co. Ltd"/OU="MIUI System Safety Team"/OU="MIUI System Safety Team"/CN="localhost"/emailAddress=xxxxxx@xiaomi.com -reqexts v3_req #自签署客户端证书 openssl x509 -req -in root/client/eomsca-req.csr -out root/client/eomsca-cert.pem -signkey root/client/eomsca-key.pem -CA root/root-cert.pem -CAkey root/root-key.pem -CAcreateserial -days 3650 #将客户端证书导出成浏览器可导入的PKCS12格式 openssl pkcs12 -export -clcerts -in root/client/eomsca-cert.pem -inkey root/client/eomsca-key.pem -out root/client/eomsca-id.p12
3.4 制作客户端证书
#创建私钥 openssl genrsa -out root/client/xm_xiaomi-key.pem 1024 #创建证书请求 openssl req -new -out root/client/xm_xiaomi-req.csr -key root/client/xm_xiaomi-key.pem -subj /C=CN/ST=BeiJing/L=BeiJing/O="Xiaomi Technologies Co. Ltd"/OU="MIUI System Safety Team"/OU="MIUI System Safety Team"/CN="localhost"/emailAddress=xxxxxx@xiaomi.com #自签署客户端证书 openssl x509 -req -in root/client/xm_xiaomi-req.csr -out root/client/xm_xiaomi-cert.pem -signkey root/client/xm_xiaomi-key.pem -CA root/client/eomsca-cert.pem -CAkey root/client/eomsca-key.pem -CAcreateserial -days 3650 #将客户端证书导出成浏览器可导入的PKCS12格式 openssl pkcs12 -export -clcerts -in root/client/xm_xiaomi-cert.pem -inkey root/client/xm_xiaomi-key.pem -out root/client/xm_xiaomi-id.p12
3.5 最终的成果
最终要用到的证书文件有3个temip-id.p12,eomsca-id.p12,xm_xiaomi-id.p12,双击它们,输入密码,可以看到证书信息,比较重要的是CN(Common Name)在本地的话一定要填写成localhost,如果是对外的可以填写成IP地址:4,编写服务器端Web Service接口
4.1 项目目录
搭建Apache CXF框架和环境的方法就忽略了,网上很多了,下面是我的服务器端demo代码结构:4.2 Web Service接口代码
package com.server; import javax.jws.WebService; /** * Web Service 接口声明 * @author liang */ @WebService(targetNamespace = "server.com") public interface HelloWorld { /** * sayHi * @param text * @return */ String sayHi(String text); }
4.3 Web Service实现类代码
package com.server; import javax.jws.WebService; /** * Web Service接口实现 * @author liang * */ @WebService(endpointInterface = "com.server.HelloWorld") public class HelloWorldImpl implements HelloWorld { @Override public String sayHi(String text) { // TODO Auto-generated method stub return "Hello, " + text; } }
4.4 spring-cxf.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:tx="http://www.springframework.org/schema/tx" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core" xmlns:wsa="http://cxf.apache.org/ws/addressing" xsi:schemaLocation=" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.1.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <cxf:bus> <cxf:features> <!--日志拦截功能,用于监控soap内容,开发后可以删除 --> <cxf:logging/> <wsa:addressing/> </cxf:features> </cxf:bus> <bean id="hello" class="com.server.HelloWorldImpl" /> <jaxws:endpoint id="helloWorld" implementor="#hello" address="/HelloWorld" publish="true"/> </beans>
4.5 web.xml内容
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name></display-name> <!-- spring需要加载的配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:com/server/spring-cxf.xml </param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <!-- cxf服务启动servlet --> <servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/service/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
4.5 服务器端总结
此时,我们还没有配置Tomcat的SSL访问设置,因此可以使用http来访问WSDL,启动Tomcat后可以在浏览器中输入http://localhost:8080/cxf-deom/service/HelloWorld?WSDL就可以看到Web Service的描述了。5,Tomcat配置SSL
现在,我们来配置服务器端的Tomcat,使得服务器端Tomcat支持SSL访问5.1,配置Tomcat
找到Tomcat的conf目录下的server.xml,搜索"8443"找到对应SSL的配置,该配置段默认是注释掉的,解除注释并修改成下面的样子:<Connector port="8443" address="0.0.0.0" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="true" sslProtocol="TLS" keystoreFile="/home/work/server/cer/temip-id.p12" keystoreType="PKCS12" keystorePass="xiaomisys" truststoreFile="/home/work/server/cer/eomsca-id.p12" truststoreType="PKCS12" truststorePass="xiaomisys"/>
修改完Tomcat配置后,重新启动Tomcat,在浏览器中输入https://localhost:8443/cxf-deom/service/HelloWorld?WSDL,会发现无法访问了。
5.2,chrome浏览器中导入证书
打开chrome的设置页面,搜索SSL,然后点击"Mange certificates",点击"import",然后导入客户端证书xm-xiaomi-id.p12,导入时会要求输入密码。再次访问https://localhost:8443/cxf-deom/service/HelloWorld?WSDL,chrome会弹出对话框,要求确认证书,点击"OK",接下来会提醒你说"该证书是不被信任的",选择"Process anyway",然后就能看到WSDL页面了。
如果最终看到了WSDL就说名Tomcat配置是正确的,证书也是正确的。
6,编写客户端程序
我们客户端要想通过HTTPS访问服务器端的Web Service服务,首先是要在客户端生成一个服务器Web Service服务的对象实例,其次是为给对象实例设置证书信息,然后就可以用这个对象实例直接调用Web Service声明的方法来访问了。6.1 客户端设置信任证书信息 -- 使用代码实现
package com.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.apache.cxf.transport.http.HTTPConduit; import com.server.HelloWorld; public class ClientUtils { private static HelloWorld helloWorld; public static HelloWorld getInstance(){ if(null != helloWorld){ return helloWorld; } try{ String addr = "https://localhost:8443/cxf-demo/service/HelloWorld"; JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean(); factoryBean.setAddress(addr); factoryBean.setServiceClass(HelloWorld.class); helloWorld = (HelloWorld) factoryBean.create(); Client proxy = ClientProxy.getClient(helloWorld); HTTPConduit conduit = (HTTPConduit) proxy.getConduit(); TLSClientParameters tlsParams = conduit.getTlsClientParameters(); if (tlsParams == null) { tlsParams = new TLSClientParameters(); } tlsParams.setDisableCNCheck(true); //设置keystore tlsParams.setKeyManagers(ClientUtils.getKeyManagers()); // 设置信任证书 tlsParams.setTrustManagers(ClientUtils.getTrustManagers()); conduit.setTlsClientParameters(tlsParams); }catch(Exception e){ e.printStackTrace(); } return helloWorld; } public static KeyManager[] getKeyManagers() { InputStream is = null; try { // 获取默认的 X509算法 String alg = KeyManagerFactory.getDefaultAlgorithm(); // 创建密钥管理工厂 KeyManagerFactory factory = KeyManagerFactory.getInstance(alg); File certFile = new File("/home/liang/Documents/works/servers/tomcat/cer/xm_xiaomi-id.p12"); if (!certFile.exists() || !certFile.isFile()) { return null; } is = new FileInputStream(certFile); // 构建以证书相应格式的证书仓库 KeyStore ks = KeyStore.getInstance("pkcs12"); // 加载证书 ks.load(is, "changeit".toCharArray()); factory.init(ks, "changeit".toCharArray()); KeyManager[] keyms = factory.getKeyManagers(); return keyms; } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } public static TrustManager[] getTrustManagers() { // 读取证书仓库输入流 InputStream is = null; try { // 信任仓库的默认算法X509 String alg = TrustManagerFactory.getDefaultAlgorithm(); // 获取信任仓库工厂 TrustManagerFactory factory = TrustManagerFactory.getInstance(alg); // 读取信任仓库 is = new FileInputStream(new File("/home/liang/Documents/works/servers/tomcat/cer/temip-id.p12")); // 密钥类型 KeyStore ks = KeyStore.getInstance("pkcs12"); // 加载密钥 ks.load(is, "changeit".toCharArray()); factory.init(ks); TrustManager[] tms = factory.getTrustManagers(); return tms; } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } }
6.2 客户端置信任证书信息 -- 使用配置文件实现
<?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:jaxws="http://cxf.apache.org/jaxws" xmlns:soap="http://cxf.apache.org/bindings/soap" xmlns:context="http://www.springframework.org/schema/context" xmlns:sec="http://cxf.apache.org/configuration/security" xmlns:http="http://cxf.apache.org/transports/http/configuration" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd 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"> <jaxws:client id="helloClient" serviceClass="com.server.HelloWorld" address="https://localhost:8443/cxf-demo/service/HelloWorld" /> <!-- *代码所的客户端可以访问 --> <http:conduit name="*.http-conduit"> <http:tlsClientParameters disableCNCheck="true"> <sec:trustManagers> <sec:keyStore type="PKCS12" password="changeit" file="/home/liang/Documents/works/servers/tomcat/cer/temip-id.p12" /> </sec:trustManagers> <!--双向认证 --> <sec:keyManagers keyPassword="changeit"> <sec:keyStore type="PKCS12" password="changeit" file="/home/liang/Documents/works/servers/tomcat/cer/xm_xiaomi-id.p12" /> </sec:keyManagers> <sec:cipherSuitesFilter> <!-- these filters ensure that a ciphersuite with export-suitable or null encryption is used, but exclude anonymous Diffie-Hellman key change as this is vulnerable to man-in-the-middle attacks --> <sec:include>.*_EXPORT_.*</sec:include> <sec:include>.*_EXPORT1024_.*</sec:include> <sec:include>.*_WITH_DES_.*</sec:include> <sec:include>.*_WITH_NULL_.*</sec:include> <sec:exclude>.*_DH_anon_.*</sec:exclude> </sec:cipherSuitesFilter> </http:tlsClientParameters> </http:conduit> </beans>
6.3 客户端通过HTTPS调用服务器Web Service服务
package com.client; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.server.HelloWorld; import com.util.ClientUtils; /** * 客户端访问服务器Web Service * @author liang * */ public final class ClientTest { public static void main(String args[]) throws Exception { test2(); } /** * 测试1,通过代码设置实例证书 */ public static void test1(){ HelloWorld client = ClientUtils.getInstance(); String response = client.sayHi("Joe"); System.out.println("Response: " + response); System.exit(0); } /** * 测试2, 通过配置文件设置实例证书 */ public static void test2(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "beans.xml"); HelloWorld client = (HelloWorld) context.getBean("helloClient"); String response = client.sayHi("Joe"); System.out.println("Response: " + response); System.exit(0); } }
以上两种方法都可以访问SSL设置的Web Service服务,然后在控制台打印输出信息。
至此,一个通过HTTPS访问Web Service的例子就搞定了。
参考 : http://liuwuhen.iteye.com/blog/1661493
相关文章推荐
- Java课程设计(基于JavaMail的C/S模式邮件客户端)总结
- UML简单介绍(二十四)——基于UML的软件设计总结
- 人人网李成:基于Cocos2d-X的游戏客户端框架设计
- 初探基于TCP的服务器/客户端结构的聊天系统(二)之应用层通信协议设计
- 基于CAB和SCSF设计智能客户端(一)翻译
- 客户端架构设计的简单总结
- 基于Hadoop开发网络云盘系统客户端界面设计初稿
- 基于SCORM标准课件的移动客户端架构设计
- 2013/10/10总结:基于GP2Y1010AUOF传感器的设计
- 基于WEB的作业管理与批阅系统的毕业论文总结一:登录模块的设计
- 基于C/S结构的高校学生网络行为规范系统的设计与实现(包含服务器与客户端)
- 《基于Android微博整合客户端的设计与实现》毕业设计论文开题报告
- 基于 J a v a S c r i p t 与 D B G R I D控件的 B / S结构客户端联想式录入技术的设计与实现
- 基于SVG的票面设计器开发总结
- 艾伟:基于web信息管理系统的权限设计分析和总结
- 基于web信息管理系统的权限设计分析和总结
- Rest – 架构风格与基于网络的软件架构设计 – 总结
- 基于CAB和SCSF设计智能客户端(二)翻译
- 基于Html5的智能家居手机客户端设计(一)——找到openhab的rest