建立安全的AXIS服务(下)
四、使用WS-Security规范对信息进行加密与身份认证
????我们打算用Handler结合WSSecurity实现Web服务安全
????设想流程:用WSClientRequestHandler.java位于客户端对客户端发出的XML文档进行加密? WSServerRequestHandler.java位于服务器端对客户端发出的加密后的XML文档进行解密
WSServerResponseHandler.java位于服务器端对服务器端返回的XML文档进行加密
WSClientResponseHandler.java位于客户端对服务器端返回的XML文档进行解密
????????????????
?1、使用ISNetworks安全提供者,ISNetworks实现了RSA加密、解密算法。
????当然,你也可以使用其它的安全提供者,并且可以使用不同的加密算法。
????ISNetworks相关包ISNetworksProvider.jar。拷贝到%TOMCAT_HOME%?????\webapps\axis\WEB-INF\lib
????
?2、Trust?Services?Integration?Kit提供了一个WS-Security实现。你可以从http://www.xmltrustcenter.org获得相关库文件,分别是ws-security.jar和tsik.jar。ws-security.jar中包含一个WSSecurity类,我们使用它来对XML进行数字签名和验证,加密与解密。同样拷贝到%TOMCAT_HOME%\webapps\axis\WEB-INF\lib
?3、创建密匙库和信任库。(见上文,一模一样!)
?????
?4、框架结构
????WSClientHandler.java??//基类,包含了一些公用方法
????WSClientRequestHandler.java?//继承于WSClientHandler.java,调用WSHelper.java对客户端发出的XML文档进行加密
????WSClientResponseHandler.java?//继承于WSClientHandler.java,调用WSHelper.java对服务器端返回的XML文档进行解密
????WSServerHandler.java?//基类,包含了一些公用方法
????WSServerRequestHandler.java?//继承于WSServerHandler.java,调用WSHelper.java对客户端发出的加密后的XML文档进行解密
????WSServerResponseHandler.java//继承于WSServerHandler.java,调用WSHelper.java对服务器端返回的XML文档进行加密
????WSHelper.java?//核心类,对SOAP消息签名、加密、解密、身份验证
????MessageConverter.java??//帮助类,Document、SOAP消息互相转换
??
?5、具体分析(在此强烈建议看一下tsik.jar的API)
????WSHelper.java?
????????static?String?PROVIDER="ISNetworks";//JSSE安全提供者。
??//添加JSSE安全提供者,你也可以使用其它安全提供者。只要支持DESede算法。这是程序里动态加载还可以在JDK中静态加载
????????static
????????{
?????????java.security.Security.addProvider(new?com.isnetworks.provider.jce.ISNetworksProvider());
????}
????/**
?????*对XML文档进行数字签名。
?????*/
????????public?static?void?sign(Document?doc,?String?keystore,?String?storetype,
????????????????????????????????????????????????String?storepass,?String?alias,?String?keypass)?throws?Exception?{
????????????????FileInputStream?fileInputStream?=?new?FileInputStream(keystore);
????????????????java.security.KeyStore?keyStore?=?java.security.KeyStore.getInstance(storetype);
????????????????keyStore.load(fileInputStream,?storepass.toCharArray());
????????????????PrivateKey?key?=?(PrivateKey)keyStore.getKey(alias,?keypass.toCharArray());
????????????????X509Certificate?cert?=?(X509Certificate)keyStore.getCertificate(alias);
????????????????SigningKey?sk?=?SigningKeyFactory.makeSigningKey(key);
????????????????KeyInfo?ki?=?new?KeyInfo();
????????????????ki.setCertificate(cert);
????????????????WSSecurity?wSSecurity?=?new?WSSecurity();//ws-security.jar中包含的WSSecurity类
????????????????wSSecurity.sign(doc,?sk,?ki);//签名。
????????}
????/**
?????*对XML文档进行身份验证。
?????*/
????????public?static?boolean?verify(Document?doc,?String?keystore,?String?storetype,
????????????????????????????????????????????????String?storepass)?throws?Exception?{
????????????????FileInputStream?fileInputStream?=?new?FileInputStream(keystore);
????????????????java.security.KeyStore?keyStore?=?java.security.KeyStore.getInstance(storetype);
????????????????keyStore.load(fileInputStream,?storepass.toCharArray());
????????????????TrustVerifier?verifier?=?new?X509TrustVerifier(keyStore);
????????????????WSSecurity?wSSecurity?=?new?WSSecurity();
????????????????MessageValidity[]?resa?=?wSSecurity.verify(doc,?verifier,?null,null);
????????????????if?(resa.length?>?0)
????????????????????????return?resa[0].isValid();
????????????????return?false;
????????}
???/**
????*对XML文档进行加密。必须有JSSE提供者才能加密。
????*/
????????public?static?void?encrypt(Document?doc,?String?keystore,?String?storetype,
????????????????????????????????????????????????String?storepass,?String?alias)?throws?Exception?{
????????????????try
????????????????{
????????????????FileInputStream?fileInputStream?=?new?FileInputStream(keystore);
????????????????java.security.KeyStore?keyStore?=?java.security.KeyStore.getInstance(storetype);
????????????????keyStore.load(fileInputStream,?storepass.toCharArray());
????????????????X509Certificate?cert?=?(X509Certificate)keyStore.getCertificate(alias);
????????????????PublicKey?pubk?=?cert.getPublicKey();
????????????????KeyGenerator?keyGenerator?=?KeyGenerator.getInstance("DESede",PROVIDER);
????????????????keyGenerator.init(168,?new?SecureRandom());
????????????????SecretKey?key?=?keyGenerator.generateKey();
????????????????KeyInfo?ki?=?new?KeyInfo();
????????????????ki.setCertificate(cert);
????????????????WSSecurity?wSSecurity?=?new?WSSecurity();
????????????????//加密。
????????????????wSSecurity.encrypt(doc,?key,?AlgorithmType.TRIPLEDES,?pubk,?AlgorithmType.RSA1_5,?ki);
????????}
????????catch(Exception?e)
????????{
????????????????e.printStackTrace();
????????}
????????}
????/**
?????*对文档进行解密。
?????*/
????????public?static?void?decrypt(Document?doc,?String?keystore,?String?storetype,
????????????????????????????????????????????????String?storepass,?String?alias,?String?keypass)?throws?Exception?{
????????????????FileInputStream?fileInputStream?=?new?FileInputStream(keystore);
????????????????java.security.KeyStore?keyStore?=?java.security.KeyStore.getInstance(storetype);
????????????????keyStore.load(fileInputStream,?storepass.toCharArray());
????????????????PrivateKey?prvk2?=?(PrivateKey)keyStore.getKey(alias,?keypass.toCharArray());
????????????????WSSecurity?wSSecurity?=?new?WSSecurity();
????????????????//解密。
????????????????wSSecurity.decrypt(doc,?prvk2,?null);
????????????????WsUtils.removeEncryptedKey(doc);//从?WS-Security?Header中删除?EncryptedKey?元素
????????}
????????public?static?void?removeWSSElements(Document?doc)?throws?Exception?{
????????????????WsUtils.removeWSSElements(doc);//?删除WSS相关的元素。
????????}
}
???WSClientHandler.java
???public?class?WSClientHandler?extends?BasicHandler{
??protected?String?keyStoreFile?;
??protected??String?keyStoreType?="JKS";//默认
??protected?String?keyStorePassword?;
??protected?String?keyAlias?;
??protected?String?keyEntryPassword?;
??protected?String?trustStoreFile?;
??protected?String?trustStoreType?=?"JKS";//默认
??protected?String?trustStorePassword?;
??protected?String?certAlias?;
??public?void?setInitialization(String?keyStoreFile,String?keyStoreType,String?keyStorePassword,
?????????????????String?keyAlias,String?keyEntryPassword,String?trustStoreFile,
?????????????????String?trustStoreType,String?trustStorePassword,String?certAlias){
??this.keyStoreFile=keyStoreFile;
??this.keyStoreType=keyStoreType;
??this.keyStorePassword=keyStorePassword;
??this.keyAlias=keyAlias;
??this.keyEntryPassword=keyEntryPassword;
??this.trustStoreFile=trustStoreFile;
??this.trustStoreType=trustStoreType;
??this.trustStorePassword=trustStorePassword;
??this.certAlias=certAlias;
}
??public?void?setInitialization(String?keyStoreFile,String?keyStorePassword,
????????????????String?keyAlias,String?keyEntryPassword,String?trustStoreFile,
????????????????String?trustStorePassword,String?certAlias){
??this.keyStoreFile=keyStoreFile;
??this.keyStorePassword=keyStorePassword;
??this.keyAlias=keyAlias;
??this.keyEntryPassword=keyEntryPassword;
??this.trustStoreFile=trustStoreFile;
??this.trustStorePassword=trustStorePassword;
??this.certAlias=certAlias;
}
??public?void?invoke(MessageContext?messageContext)?throws?AxisFault?{//在这个方法里对XML文档进行处理
????//do?nothing?now!
??}
??public?void?onFault(MessageContext?msgContext)?{
????System.out.println("处理错误,这里忽略!");
????????}
}
??
??WSClientRequestHandler.java
?
??public?void?invoke(MessageContext?messageContext)?throws?AxisFault?{
????try?{
?????SOAPMessage?soapMessage?=?messageContext.getMessage();
?????Document?doc?=?MessageConverter.convertSoapMessageToDocument(soapMessage);?//soapMessage转换为Document
?????WSHelper.sign(doc,?keyStoreFile,?keyStoreType,keyStorePassword,?keyAlias,?keyEntryPassword);?//数字签名
?????WSHelper.encrypt(doc,?trustStoreFile,?trustStoreType,?trustStorePassword,?certAlias);?//加密
?????soapMessage?=?MessageConverter.convertDocumentToSOAPMessage(doc);?
//处理后的Document再转换回soapMessage
?????messageContext.setMessage(soapMessage);
?????}?catch?(Exception?e){
?????System.err.println("在处理响应时发生以下错误:?"?+?e);
??????e.printStackTrace();??}
????????}
}?
??WSClientResponseHandler.java
?
??public?void?invoke(MessageContext?messageContext)?throws?AxisFault?{
????try?{
????????????SOAPMessage?soapMessage?=??messageContext.getCurrentMessage();
????????????Document?doc?=?MessageConverter.convertSoapMessageToDocument(soapMessage);
????????WSHelper.decrypt(doc,?keyStoreFile,?keyStoreType,
????????????????????????????keyStorePassword,?keyAlias,?keyEntryPassword);//解密
????????????WSHelper.verify(doc,?trustStoreFile,?trustStoreType,?trustStorePassword);//验证
????????????WSHelper.removeWSSElements(doc);
????????????soapMessage?=?MessageConverter.convertDocumentToSOAPMessage(doc);
????????????messageContext.setMessage(soapMessage);
????}?catch?(Exception?e){
????????????e.printStackTrace();
????????????System.err.println("在处理响应时发生以下错误:?"?+?e);
?????????????????????????}
????????}
}?
???
???WSServerHandler.java?
???
??protected?String?keyStoreFile?;
??protected??String?keyStoreType?="JKS";//默认
??protected?String?keyStorePassword?;
??protected?String?keyAlias?;
??protected?String?keyEntryPassword?;
??protected?String?trustStoreFile?;
??protected?String?trustStoreType?=?"JKS";//默认
??protected?String?trustStorePassword?;
??protected?String?certAlias?;
??public?void?invoke(MessageContext?messageContext)?throws?AxisFault?{
????//do?nothing?now!
??}
??public?void?onFault(MessageContext?msgContext)?{
????System.out.println("处理错误,这里忽略!");
????????}
??public?void?init()?{?//初始化,从配置文件server-config.wsdd中读取属性
????keyStoreFile?=?(String)getOption("keyStoreFile");
????if((?keyStoreFile==?null)?)
??????System.err.println("Please?keyStoreFile?configured?for?the?Handler!");
????trustStoreFile?=?(String)getOption("trustStoreFile");
?????if((??trustStoreFile==?null)?)
????System.err.println("Please?trustStoreFile?configured?for?the?Handler!");
????keyStorePassword?=?(String)getOption("keyStorePassword");
?????if((?keyStorePassword==?null)?)
????System.err.println("Please?keyStorePassword?configured?for?the?Handler!");
????keyAlias?=?(String)getOption("keyAlias");
?????if((?keyAlias==?null)?)
????System.err.println("Please?keyAlias?configured?for?the?Handler!");
????keyEntryPassword?=?(String)getOption("keyEntryPassword");
?????if((?keyEntryPassword==?null)?)
????System.err.println("Please?keyEntryPassword?configured?for?the?Handler!");
????trustStorePassword?=?(String)getOption("trustStorePassword");
?????if((?trustStorePassword==?null)?)
????System.err.println("Please?trustStorePassword?configured?for?the?Handler!");
????certAlias?=?(String)getOption("certAlias");
????if?((certAlias==null))
????????System.err.println("Please?certAlias?configured?for?the?Handler!");
????if?((getOption("keyStoreType"))?!=?null)
???????keyStoreType?=?(String)getOption("keyStoreType");
????if?((getOption("trustStoreType"))?!=?null)
???????trustStoreType?=?(String)getOption("trustStoreType");
????}
}?
????
????
????WSServerRequestHandler.java?
??public?void?invoke(MessageContext?messageContext)?throws?AxisFault?{
????try?{
??????SOAPMessage?msg?=?messageContext.getCurrentMessage();
????????????Document?doc?=?MessageConverter.convertSoapMessageToDocument(msg);
????????????System.out.println("接收的原始消息:");
???????????msg.writeTo(System.out);
????????WSHelper.decrypt(doc,?keyStoreFile,?keyStoreType,
????????????????????????????keyStorePassword,?keyAlias,?keyEntryPassword);//解密
????????????WSHelper.verify(doc,?trustStoreFile,?trustStoreType,?trustStorePassword);//验证
????????????WSHelper.removeWSSElements(doc);
????????????msg?=?MessageConverter.convertDocumentToSOAPMessage(doc);
????????????System.out.println("怀原后的原始消息:");
????????????msg.writeTo(System.out);
????????????messageContext.setMessage(msg);
????}?catch?(Exception?e){
????????????e.printStackTrace();
????????????System.err.println("在处理响应时发生以下错误:?"?+?e);
?????????????????????????}
????????}
}???
?????
?????WSServerResponseHandler.java
??public?void?invoke(MessageContext?messageContext)?throws?AxisFault?{
????try?{
?????SOAPMessage?soapMessage?=?messageContext.getMessage();
???????System.out.println("返回的原始消息:");
?????????soapMessage.writeTo(System.out);
??????Document?doc?=?MessageConverter.convertSoapMessageToDocument(soapMessage);
????????WSHelper.sign(doc,?keyStoreFile,?keyStoreType,
??????????keyStorePassword,?keyAlias,?keyEntryPassword);//数字签名
???????WSHelper.encrypt(doc,?trustStoreFile,?trustStoreType,//加密
????????trustStorePassword,?certAlias);
???????soapMessage?=?MessageConverter.convertDocumentToSOAPMessage(doc);
???????System.out.println("返回的加密后的消息:");
???????soapMessage.writeTo(System.out);
???????messageContext.setMessage(soapMessage);
????????}?catch?(Exception?e){
????????System.err.println("在处理响应时发生以下错误:?"?+?e);
?????????e.printStackTrace();
?????????}
????????}
}
6、应用
???为方便使用,把上述文件打包为ws-axis.jar,放入%TOMCAT_HOME%\webapps\axis\WEB-INF\lib
???
???1)把HelloWorld重新部署一次,在server-config.wsdd中修改如下部署代码。
?????????<parameter?name="allowedMethods"?value="*"/>
?????????<parameter?name="className"?value="HelloWorld"/>
?????????<requestFlow>
???????????<handler?type="soapmonitor"/>
???????????<handler?type="java:com.ronghao.WSAxis.WSServerRequestHandler">
??????????????<parameter?name="keyStoreFile"?value="f:\server.keystore"/>
??????????????<parameter?name="trustStoreFile"?value="f:\server.truststore"/>
??????????????<parameter?name="keyStorePassword"?value="changeit"/>
??????????????<parameter?name="keyAlias"?value="Server"/>
??????????????<parameter?name="keyEntryPassword"?value="changeit"/>
??????????????<parameter?name="trustStorePassword"?value="changeit"/>
??????????????<parameter?name="certAlias"?value="clientkey"/>
???????????</handler>
????????</requestFlow>
????????<responseFlow>
???????????<handler?type="soapmonitor"/>
???????????<handler?type="java:com.ronghao.WSAxis.WSServerResponseHandler">
??????????????<parameter?name="keyStoreFile"?value="f:\server.keystore"/>
??????????????<parameter?name="trustStoreFile"??value="f:\server.truststore"/>
??????????????<parameter?name="keyStorePassword"?value="changeit"/>
??????????????<parameter?name="keyAlias"?value="Server"/>
??????????????<parameter?name="keyEntryPassword"?value="changeit"/>
??????????????<parameter?name="trustStorePassword"?value="changeit"/>
??????????????<parameter?name="certAlias"?value="clientkey"/>
???????????</handler>
????????</responseFlow>
?????</service>
?????
????2)修改客户端程序?TestClient.java(修改的部分已标出,记着导入ws-axis.jar)
????import?org.apache.axis.client.Call;
????import?org.apache.axis.client.Service;
????import?com.ronghao.WSAxis.*;
????
????public?class?WSSClient1
{
????public?static?void?main(String?[]?args)
????{
????????try?{
????????????????//服务端的url,需要根据情况更改。
????????????String?endpointURL?=?"http://localhost:8080/axis/services/HelloWorld";
????????????Service?svc?=?new?Service();
????????????WSClientHandler?handler=new?WSClientRequestHandler();
//注意新加的HANDLER
????????????handler.setInitialization("f:/client.keystore","changeit","Client","changeit",
????????????????"f:/client.truststore","changeit","serverkey");//初始化
????????????WSClientHandler?handlee=new?WSClientResponseHandler();
//注意新加的HANDLER
????????????handlee.setInitialization("f:/client.keystore","changeit","Client","changeit",
????????????????"f:/client.truststore","changeit","serverkey");//初始化
?????????????????????Call?call?=(Call)svc.createCall();
?????????????????????call.setClientHandlers(handler,handlee);//添加Handler
?????????????????????call.setTargetEndpointAddress(new?java.net.URL(endpointURL));
?????????????????????call.setOperationName(new?QName("sayHello"));
?????????????????????String?result?=?(String)?call.invoke(?new?Object?[]?{});
?????????????????????System.out.println("the?result"+result);
????????}?catch?(Exception?e)?{
????????????????e.printStackTrace();
????????}
????}
}
???运行的时候http://localhost:8080/axis/SOAPMonitor中看到的请求的XML就已加密!
???
总结
???这里对代码的解释是不够的,很多概念没有提到。建议你最好看tsik.jar和AXIS的API深入了解。另外对ws-axis.jar的加解密实现打算运用apache的wss4j,相关网址http://ws.apache.org/ws-fx/wss4j/。不过这个东西也应该够用了暂时。
?