假设我已经使用cxf构建了基于密码验证的webService ,此时只有给出正确的用户名和密码才能调用我的webservice,但这个webservice还不是绝对安全,因为走的是http,如果中间有人抓了http包,他看到明文的用户名密码,他就可以假装自己是正常用户,来调用我的webservice。为了防止信息已明文传送,再使用ssl加密,走https。
公钥和私钥是一对数字。公钥加密的东西只有私钥才能解密。把公钥公开分发给任何人,当他们要同我们联系时,把信息用公钥加密,此时只有拥有私钥的人才能解密。如果有人截获了信息,但他没有私钥,他也不知道发了什么。公钥和私钥是对称的,服务器返回数据时也可以用私钥加密,客户端用公钥来验证是否是服务端返回的数据,这样就保证了安全性。
alice使用随机数生成一对公钥私钥,分发公钥出去。
bob要给alice发送信息,就用公钥加密,Alice收到后用私钥解密就可以看到了
alice返回信息的时候,也可以用私钥加密,顺便生成签名。bob用公钥解密就可以验证是否是alice返回的数据。
证书除开包含公钥外,还有发布公钥的人的信息。有密钥对就可以生成证书。不过这个是自己生成的,并没有去权威机构认证,所以浏览器访问的时候会报安全警告。从网上获取到别人的证书,就可以拿到他的公钥。
java提供了工具来生成密钥对、证书。
#生成keystore文件,既密钥文件 730天。还可以填些个人信息, keytool -genkeypair -validity 730 -alias myservicekey \ -keystore serviceKeystore.jks -dname "cn=hutao,ou=eloan,o=wits,l=wuhan,c=cn" \ -keypass xiaotaozi -storepass xiaotaozi keytool -genkeypair -validity 730 -alias myclientkey -keystore clientKeystore.jks \ -keypass ckpass -storepass cspass #导出服务端的证书 keytool -export -rfc -keystore serviceKeystore.jks -alias myservicekey \ -file MyService.cer -storepass tianyixiaodai #把服务端导入客户端的信任库 keytool -import -noprompt -trustcacerts -file MyService.cer -alias myservicekey \ -keystore clientKeystore.jks -storepass cspass #导出客户端的证书 keytool -export -rfc -keystore clientKeystore.jks -alias myclientkey -file MyClient.cer \ -storepass cspass keytool -import -noprompt -trustcacerts -file MyClient.cer -alias myclientkey \ -keystore serviceKeystore.jks -storepass tianyixiaodai
我们不关心服务器是否是自己的服务器,所以不需要给客户端也生成证书,上面的命令只执行服务器端的。如果是双向认证,则客户端也要生成,且相互添加信任,上面的命令全部要执行。
-keystore 和 -storepass分别是当前密钥的密码和当前密钥文件的操作密码,最好设置一样,我这里不一样的话,tomcat无法部署。keystore就是密码,storepass就是你有这个密码,才能读取这个密钥文件。
如果我们是调用别人的webservice,可以用浏览器打开wsdl,下载证数,利用import命令生成一个客户端密钥文件。然后用该密钥文件就可以调用了。
主要是要配置tomcat。首先利用上面的第一条命令生成密钥文件serviceKeystore.jks 。首先打开tomcat, server.xml
<Connector SSLEnabled="true" clientAuth="false" keystoreFile="C:\\Users\\samsung\\serviceKeystore.jks" keystorePass="xiaotaozi" maxThreads="150" port="8443" protocol="org.apache.coyote.http11.Http11Protocol" scheme="https" secure="true" sslProtocol="TLS"/>
打开8443端口上的连接器,如果有需要ssl的连接进来,tomcat会自动转发到这个端口。 keystorePass就是密钥文件的操作密码。我这里sslProtocol是TLS。 clientAuth=false表示只需要单向认证,不需要客户端也给我他的公钥,如果只是加密,单项已经足够。
然后配置tomcat web.xml,设置哪些url需要使用https。
<login-config> <auth-method>CLIENT-CERT</auth-method> <realm-name>Client Cert Users-only Area</realm-name> </login-config> <security-constraint> <web-resource-collection> <web-resource-name>SSL</web-resource-name> <url-pattern>/witsws/*</url-pattern> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint>
因为我的webService地址都以witsws开头,所以设置成这样。
启动tomcat,访问8443端口,会出现,证书不被信任的提示信息,表示已经部署成功。如果用http访问witsws下的路径会自动被转成https。
以调用上面的webservice为例。 在浏览器中下载证书。假设证书保存的名字为MyService.cer运行下面的命令
keytool -import -noprompt -trustcacerts -file MyService.cer -alias myservicekey \ -keystore clientKeystore.jks -storepass cspass
此时会要求你输入自己的密码,我输123456,并生成本地的密钥文件。
在cxf客户端配置自己的密钥文件之后就可以访问对方的webservice。写一个cxf-client-test.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:http="http://cxf.apache.org/transports/http/configuration" xmlns:sec="http://cxf.apache.org/configuration/security" xsi:schemaLocation="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 http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd"> <jaxws:client id="testCamClient" serviceClass="com.xxx.wesb.cam.webservice.CamService" address="https://127.0.0.1:8443/xxx-esb/witsws/camService"/> <http:conduit name="*.http-conduit"> <!--这里简便配法,详细的参考官方文档--> <!--注意这里的secureSocketProtocol 要和tomcat一模一样--> <http:tlsClientParameters disableCNCheck="true" secureSocketProtocol="TLS"> <!--自己的密钥 单向调用别人的时候,这里不用配置 如果是双向的,配上自己的 <sec:keyManagers keyPassword="123456"> <sec:keyStore type="JKS" password="123456" file="C:\\Users\\samsung\\clientKeystore.jks" /> </sec:keyManagers>--> <!--这里填写我们刚刚生成的密钥文件--> <sec:trustManagers> <sec:keyStore type="JKS" password="123456" file="C:\\Users\\samsung\\clientKeystore.jks" /> </sec:trustManagers> <sec:cipherSuitesFilter> <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>
然后写个单元测试,就可以调用了
public class TestCreditQuery extends TestCase { public void testCreditQuery() { ApplicationContext app = new ClassPathXmlApplicationContext("testwsconfig/cxf-client-test.xml"); CamService ws = (CamService) app.getBean("testCamClient"); String xml = ws.service("xxxxxx"); System.out.println(xml); } }
如果报错,fatal alert handshake 可以判定是相互的密钥没法配对,可能是版本、或者协议不匹配。也可能没相互导入信任。如果是sec的那些类直接没法实例化,基本是密钥文件错误,或者密钥文件操作密码写错。
如果是双向验证,则在客户端也生成证书,修改tomcat连接器设置,修改cxf,双方都加上对方和自己的密钥文件。