四、使用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/ 。不过这个东西也应该够用了暂时。
http://www.blogjava.net/ronghao 荣浩原创,转载请注明出处:)