SOAP WebService以CXF实现WS-Security之xml签名及加密
接上篇,记录一下使用apache cxf和spring使用自签名数字证书实现WebService服务端及客户端的xml签名、加密以及解密和签名验证;
这里仅针对客户端加密和签名并在服务端实现解密及签名验证的单向认证的情形,双向认证可以参照官方sample改进;
实现:
客户端使用客户端私钥进行消息签名、客户端使用服务端公钥消息加密;
服务端使用客户端公钥进行签名验证、服务端使用服务端私钥进行消息解密;
对于xml签名流程理论参考: http://www.ibm.com/developerworks/cn/xml/x-cn-java6xmlsignature/
基本流程:
一、密钥库生成(比较关键)
1,生成客户端用于xml签名的证书密钥库,生成服务端用于解密的私钥库
2,导出客户端证书、导出服务端证书;
3,客户端证书导入并生成供服务端使用的信任证书库,供服务端验证签名;
4,服务端证书导入并生成供客户端使用的信任证书库,供客户端加密SOAP消息;
keytool -genkey -alias merrickclientprivate -keypass passwd -keystore client_keystore.jks -storepass passwd -dname "CN=merrick.site" -keyalg RSA
keytool -selfcert -keystore client_keystore.jks -alias merrickclientprivate -storepass passwd -keypass passwd
keytool -export -alias merrickclientprivate -file client_pubcert.cer -keystore client_keystore.jks -storepass passwd
keytool -genkey -alias merrickserverprivate -keypass passwd -keystore server_keystore.jks -storepass passwd -dname "CN=merrick.site" -keyalg RSA
keytool -selfcert -keystore server_keystore.jks -alias merrickserverprivate -storepass passwd -keypass passwd
keytool -export -alias merrickserverprivate -file server_pubcert.cer -keystore server_keystore.jks -storepass passwd
keytool -import -alias merrickclientpublic -file client_pubcert.cer -keystore server_trust.jks -storepass passwd
keytool -import -alias merrickserverpublic -file server_pubcert.cer -keystore client_trust.jks -storepass passwd
二、WebProject服务端
[WebService开放接口及ServiceBean见前篇有关usernametoken认证:http://fall10.iteye.com/blog/2334924]
1,web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>cxfjaxwssecurityserver1</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/beans.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <description>Apache CXF Endpoint</description> <display-name>cxf</display-name> <servlet-name>cxf</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>cxf</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>60</session-timeout> </session-config> </web-app>
2,spring配置
<?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:util="http://www.springframework.org/schema/util" 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://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath*:META-INF/cxf/cxf-extension-*.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <bean id="logIn" class="org.apache.cxf.interceptor.LoggingInInterceptor" /> <bean id="logOut" class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> <bean id="saajIn" class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" /> <bean id="mypasswdcallback" class="cxf.jaxws.basic.server.PasswdCallback"/> <bean id="wss4jin" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="Signature Encrypt"/> <entry key="passwordCallbackRef" > <ref bean="mypasswdcallback"/> </entry> <entry key="decryptionPropFile" value="../prop/server_decryption.properties" /><!-- 基于java类路径 --> <entry key="signaturePropFile" value="../prop/server_versign.properties"/> </map> </constructor-arg> </bean> <jaxws:endpoint id="bookinfoservice" address="/bookinfo" implementor="cxf.jaxws.basic.server.BookServiceImpl" > <jaxws:outInterceptors> <ref bean="logOut"/> </jaxws:outInterceptors> <jaxws:inInterceptors> <ref bean="logIn"/> <ref bean="saajIn"/> <ref bean="wss4jin"/> </jaxws:inInterceptors> </jaxws:endpoint> </beans>
3,密码验证回调函数(虽说property文件中已配置,但是还是需要设置一下各密钥用户的密码)
public class PasswdCallback implements CallbackHandler { @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { WSPasswordCallback pc = (WSPasswordCallback)callbacks[i]; pc.setPassword("passwd");/**使用数字证书加密、签名,同样也必须在这里设置密钥库的密码*/ System.out.println("Server " + pc.getIdentifier()); System.out.println("Server " + pc.getPassword()); } } }
3,服务端使用密钥库相关信息属性配置文件
server_versign.properties//验证签名用公钥信息
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=passwd org.apache.ws.security.crypto.merlin.keystore.alias=merrickclientpublic org.apache.ws.security.crypto.merlin.keystore.file=../keys/server_trust.jks
server_decryption.properties//解密用私钥信息
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=passwd org.apache.ws.security.crypto.merlin.keystore.alias=merrickserverprivate org.apache.ws.security.crypto.merlin.keystore.file=../keys/server_keystore.jks
三、Java Webservice客户端
[客户端WebServiceClient及ServiceBean见前篇]
1,纯java客户端调用代码
public static void clientwith_UserTokenAuth_withoutSpring() throws Throwable { Map<String, Object> outProps = new HashMap<String, Object>(); outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.ENCRYPT ); outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, "autoclient.test.ClientPasswdCallback"); outProps.put(WSHandlerConstants.SIG_PROP_FILE,"autoclient/test/clientside_sign.properties"); outProps.put(WSHandlerConstants.SIGNATURE_USER, "merrickclientprivate"); outProps.put(WSHandlerConstants.ENCRYPTION_USER, "merrickserverpublic"); outProps.put(WSHandlerConstants.ENC_PROP_FILE, "autoclient/test/clientside_enc.properties"); JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(cxfclient.jaxws.auto.BookService.class); factory.setAddress("http://localhost:8080/cxfjaxwssecurityserver1/services/bookinfo"); factory.getOutInterceptors().add(new WSS4JOutInterceptor(outProps)); cxfclient.jaxws.auto.BookService service = (cxfclient.jaxws.auto.BookService) factory.create(); DtoBean b = new DtoBean(); b.setId(1); b.setName("a"); String response = service.getOneBookInfo(b); System.out.println(response); }
设定密码的回调方法
public class ClientPasswdCallback implements CallbackHandler { private Map<String,String> passwds = new HashMap<String,String>(); public ClientPasswdCallback() { } @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { WSPasswordCallback pc = (WSPasswordCallback)callbacks[i]; pc.setPassword("passwd"); /**使用数字证书加密、签名,同样也必须在这里设置密钥库的密码*/ System.out.println("Client " + pc.getIdentifier()); System.out.println("Client " + pc.getPassword()); } } }2,客户端密钥配置属性文件
clientside_enc.properties//加密用
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=passwd org.apache.ws.security.crypto.merlin.keystore.alias=merrickserverpublic org.apache.ws.security.crypto.merlin.keystore.file=autoclient/test/client_trust.jks
clientside_sign.properties//签名用
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=passwd org.apache.ws.security.crypto.merlin.keystore.alias=merrickclientprivate org.apache.ws.security.crypto.merlin.keystore.file=autoclient/test/client_keystore.jks
四、客户端发起Webservice请求测试
在服务端可见log
信息: Inbound Message ---------------------------- ID: 1 Address: http://localhost:8080/cxfjaxwssecurityserver1/services/bookinfo Encoding: UTF-8 Http-Method: POST Content-Type: text/xml; charset=UTF-8 Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], content-type=[text/xml; charset=UTF-8], host=[localhost:8080], pragma=[no-cache], SOAPAction=[""], transfer-encoding=[chunked], user-agent=[Apache CXF 3.1.6]} Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1"><xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="EK-7424b921-ca58-4c0c-925c-69efcf11de8e"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><wsse:SecurityTokenReference><ds:X509Data><ds:X509IssuerSerial><ds:X509IssuerName>CN=merrick.site</ds:X509IssuerName><ds:X509SerialNumber>524604543</ds:X509SerialNumber></ds:X509IssuerSerial></ds:X509Data></wsse:SecurityTokenReference></ds:KeyInfo><xenc:CipherData><xenc:CipherValue>iW2uP138lAETFgb8X7mEZabg0hrdHP9wBoGiNza7RZLkrYTd8zFiHN3rcUek+6DnWIJMINHkH6W5jjs/ijbPjr5/3bHGATU5MjIPkZXlTOdS0SRPO9pKEJg/yJ75jxiWRTJI+ji59PbCUR4bzPKB1by/pzLkVdaJBdqr07usAybk7/8WXy0LgN2NSMAFaDyZIv/d3C2Qxd1fYp+noDhQ0u0+6yYi3tbH4VKUKyRea19/B+LwwZSpXsooGXXjf7NY+NCl9+c5lXpbJd+c5Tl+vGHqspAhINhO4Pylhxen2CT8Vnt192bUBflTe2wHL/jampSMZ2cQR6REbjsw0vUMRA==</xenc:CipherValue></xenc:CipherData><xenc:ReferenceList><xenc:DataReference URI="#ED-5e63bd7f-3156-4e11-92aa-f88b81391d2e"/></xenc:ReferenceList></xenc:EncryptedKey><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-c05f922e-e2e6-403a-9006-603f5ebc68a8"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soap"/></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ds:Reference URI="#id-48c8406f-5918-42ba-9315-af0078a34f82"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>oQetPKFHYFuzDqTIW2pwtNb7YOs=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>BrnWlHeUzwDkps7JO+DYwup2JiD6tOjI/B1xDjTri2kFa0wk/Y7xHIGtxiu+9YWT4iC0UeFtIYXuABi7+j75Qv+fUJ/PsYBrwBz4SHEuDy9ydMY12UwMJWys7zXzIoRAjSmHMX+qnejzrtRuy5Z+joZmNV0tmT2Lm9tft8pNAewhefea7Xj2ylYGSZ5XjZrx0vAru3AZiAwtMSkd/zgDKV52B4jF6Wo3Y5a57AEjvkqjc87x0WQzeqfjM9CStYSVAFgOAQXiMGe6uk6zHd7J/Viu3aoIfbEVe4wBfXeAGjr7kX6w25OvQMWb7WiY/hxggTwVxtTutLtUx42PpdXtow==</ds:SignatureValue><ds:KeyInfo Id="KI-06fd1e1c-73b6-4851-b8c1-56836c6dd89f"><wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="STR-40b02bc9-43e7-40cf-9d3d-408c5d00d06b"><ds:X509Data><ds:X509IssuerSerial><ds:X509IssuerName>CN=merrick.site</ds:X509IssuerName><ds:X509SerialNumber>374919786</ds:X509SerialNumber></ds:X509IssuerSerial></ds:X509Data></wsse:SecurityTokenReference></ds:KeyInfo></ds:Signature></wsse:Security></SOAP-ENV:Header><soap:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-48c8406f-5918-42ba-9315-af0078a34f82"><xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-5e63bd7f-3156-4e11-92aa-f88b81391d2e" Type="http://www.w3.org/2001/04/xmlenc#Content"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey"><wsse:Reference URI="#EK-7424b921-ca58-4c0c-925c-69efcf11de8e"/></wsse:SecurityTokenReference></ds:KeyInfo><xenc:CipherData><xenc:CipherValue>yFthTTriWkD4gbX4UyvCs6hixP2y5i1fBwAF/jmYbyTuYpObB9H98ACFxnLQEbje6v4Dg6H15R2OwNdHGwe57aWGHl8maT4bUUMWofd1Df6Mu7zhyUjKjpcJUv6qBNB4Bt94u4obsTXvmp7ayfJR3RNRxcrmkC/76bVUMM3Z6ze6716H3YfOd026HuA1Ru5b</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData></soap:Body></soap:Envelope> -------------------------------------- Server merrickserverprivate Server passwd ---Invoking getOneBookInfo, DTOBean: 1,a 十一月 08, 2016 10:12:39 上午 org.apache.cxf.services.BookServiceImplService.BookServiceImplPort.BookService 信息: Outbound Message --------------------------- ID: 1 Response-Code: 200 Encoding: UTF-8 Content-Type: text/xml Headers: {} Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getOneBookInfoResponse xmlns:ns2="http://server.basic.jaxws.cxf/"><return><<A Tale of two citiest>>,2001,English</return></ns2:getOneBookInfoResponse></soap:Body></soap:Envelope> --------------------------------------
已有 0 人发表留言,猛击->> 这里<<-参与讨论
ITeye推荐