列宿乃施于上荣兮 日月才经于柍桭

………… 白驹过隙 岁月如斯 梦萦回绕 心灵之声
posts - 57, comments - 8, trackbacks - 0, articles - 41
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

基于Axis 1.X的Web Service开发(三)

Posted on 2007-04-07 15:57 默认为零 阅读(631) 评论(1)  编辑  收藏 所属分类: 技术

  前段时间摸索过基于信息安全的Web service开发,今天休息闲着也憋得慌,正好整理一下(参考网络资料)。这里使用了WS-Security规范对SOAP包进行加密与身份认证。
      
  首先从http://www.xmltrustcenter.org获得相关库文件,分别是ws-security.jar和tsik.jar。ws-security.jar中包含一个WSSecurity类,我们使用它来对XML进行数字签名和验证,加密与解密。
  这里使用ISNetworks安全提供者,ISNetworks实现了RSA加密、解密算法。参考相关包ISNetworksProvider.jar。

  使用JDK自带的工具创建密匙库和信任库,因为在请求和响应过程中都需要使用,所以需要生成两对。建立批处理文件gen-cer-store.bat内容如下:

   set SERVER_DN="CN=Server, OU=ec, O=ec, L=BEIJINGC, S=BEIJING, C=CN"
   set CLIENT_DN
="CN=Client, OU=ec, O=ec, L=BEIJING, S=BEIJING, C=CN"
   set KS_PASS
=-storepass changeit
   set KEYINFO
=-keyalg RSA
   
   keytool -genkey -alias Server -dname %SERVER_DN% %KS_PASS% -keystore server.keystore %KEYINFO% -keypass changeit
   keytool -export -alias Server -file test_axis.cer %KS_PASS% -keystore server.keystore
   keytool -import -file test_axis.cer %KS_PASS% -keystore client.truststore -alias serverkey -noprompt
   
   keytool -genkey -alias Client -dname %CLIENT_DN% %KS_PASS% -keystore client.keystore %KEYINFO% -keypass changeit
   keytool -export -alias Client -file test_axis.cer %KS_PASS% -keystore client.keystore
   keytool -import -file test_axis.cer %KS_PASS% -keystore server.truststore -alias clientkey -noprompt

  这里具体内容含义不解释了,自己可以在网络上找来看看。
  执行gen-cer-store.bat之后,我们可以得到四个文件server.keystore,server.truststore,client.keystore,client.truststore。

  
基本流程如下: 
    1
、 客户端(WSSClient)发出调用Web服务请求; 
    2
、 客户端HandlerWSClientRequestHandler)截获请求的SOAP消息; 
    3
、 客户端Handler对截获的SOAP消息进行数字签名(使用client.keystore作为签名依据); 
    4
、 客户端Handler对签名后的SOAP消息进行加密(使用RSA算法加密); 
    5
、 被加密的SOAP消息通过互联网传送到目标Web服务端口; 
    6
、 服务器端HandlerWSServerRequestHandler)截获加密的SOAP消息; 
    7
、 服务器端Handler对加密的SOAP消息进行解密; 
    8
、 服务器端HandlerSOAP消息进行身份验证(server.truststore包含了所信任的身份信息),如果验证不通过,将抛出异常; 
    9
、 服务器端Handler删除被解密后的SOAP消息中与WS-Security相关的元素; 
    10
、 解密后的原始SOAP消息被发送到目标Web服务端口;
    11
、 目标Web服务对Web服务请求进行处理,然后返回响应的SOAP消息; 
    12
、 服务器端Handler(WSServerResponseHandler)截获响应的SOAP消息; 
    13
、 服务器端Handler对截获的SOAP消息进行数字签名(使用server.keystore作为签名依据); 
    14
、 服务器端Handler对签名后的SOAP消息进行加密(使用RSA算法加密); 
    15
、 被加密的SOAP消息通过互联网传送到目客户端; 
    16
、 客户端Handler(WSClientResponseHandler)截获加密的SOAP消息; 
    17
、 客户端Handler对加密的SOAP消息进行解密; 
    18
、 客户端HandlerSOAP消息进行身份验证(client.truststore包含了所信任的身份信息),如果验证不通过,将抛出异常; 
    19
、 客户端Handler删除被解密后的SOAP消息中与WS-Security相关的元素; 
    20
、 被解密后的SOAP消息发送到目标客户端,客户端输出调用结果。
  
从上面流程可以看出,在一个SOAP调用回合中,需要对SOAP消息进行四次处理,在请求和响应中都是签名” -> “加密” -> “解密” -> “验证的过程。

  
程序框架如下:

 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文档进行加密
 WSSecurityHelper.java //核心类,对SOAP消息签名、加密、解密、身份验证
 MessageConverter.java //帮助类,Document、SOAP消息互相转换


  WSSecurityHelper.java主要包括方法如下:

    /**
     * 数字签名
     
*/

    
public static void sign(Document doc, String keystore, String storetype,
            String storepass, String alias, String keypass) 
throws Exception {
        InputStream is 
= WSSecurityHelper.class.getResourceAsStream(keystore);
        KeyStore keyStore 
= KeyStore.getInstance(storetype);
        keyStore.load(is, 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 security 
= new WSSecurity();
        security.sign(doc, sk, ki);
    }


    
/**
     * 身份验证
     
*/

    
public static boolean verify(Document doc, String keystore,
            String storetype, String storepass) 
throws Exception {
        InputStream is 
= WSSecurityHelper.class.getResourceAsStream(keystore);
        KeyStore keyStore 
= KeyStore.getInstance(storetype);
        keyStore.load(is, storepass.toCharArray());

        TrustVerifier verifier 
= new X509TrustVerifier(keyStore);

        WSSecurity security 
= new WSSecurity();
        MessageValidity[] resa 
= security.verify(doc, verifier, nullnull);
        
if (resa.length > 0{
            
return resa[0].isValid();
        }

        
return false;
    }


    
/**
     * 加密
     
*/

    
public static void encrypt(Document doc, String keystore, String storetype,
            String storepass, String alias) 
throws Exception {
        
try {
            InputStream is 
= WSSecurityHelper.class
                    .getResourceAsStream(keystore);
            KeyStore keyStore 
= KeyStore.getInstance(storetype);
            keyStore.load(is, storepass.toCharArray());
            X509Certificate cert 
= (X509Certificate) keyStore
                    .getCertificate(alias);
// serverkey
            KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede",
                    PROVIDER);

            keyGenerator.init(
168new SecureRandom());
            SecretKey key 
= keyGenerator.generateKey();

            KeyInfo ki 
= new KeyInfo();
            ki.setCertificate(cert);

            PublicKey pubk 
= cert.getPublicKey();
            WSSecurity security 
= new WSSecurity();
            security.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 {
        InputStream is 
= WSSecurityHelper.class.getResourceAsStream(keystore);
        java.security.KeyStore keyStore 
= java.security.KeyStore
                .getInstance(storetype);
        keyStore.load(is, storepass.toCharArray());
        PrivateKey prvk2 
= (PrivateKey) keyStore.getKey(alias, keypass
                .toCharArray());

        WSSecurity security 
= new WSSecurity();
        security.decrypt(doc, prvk2, 
null);
        removeWSSElements(doc);
    }


  MessageConverter.java
实现如下:

package com.macrowing.sys.security;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 * Document对象与SOAPMessage对象相互转换
 
*/

public class MessageConverter {

    
/**
     * Document转换成SOAPMessage
     
*/

    
public static SOAPMessage convertDocumentToSOAPMessage(Document doc)
            
throws TransformerConfigurationException, TransformerException,
            SOAPException, IOException 
{
        TransformerFactory transformerFactory 
= TransformerFactory
                .newInstance();
        Transformer transformer 
= transformerFactory.newTransformer();

        ByteArrayOutputStream byteArrayOutputStream 
= new ByteArrayOutputStream();
        transformer.transform(
new DOMSource(doc), new StreamResult(
                byteArrayOutputStream));
        MimeHeaders header 
= new MimeHeaders();
        header.addHeader(
"Content-Type""text/xml");
        MessageFactory factory 
= MessageFactory.newInstance();
        SOAPMessage soapMsg 
= factory.createMessage(header,
                
new ByteArrayInputStream(byteArrayOutputStream.toByteArray(),
                        
0, byteArrayOutputStream.size()));
        
return soapMsg;
    }


    
/**
     * SOAPMessage转换成Document
     
*/

    
public static Document convertSoapMessageToDocument(SOAPMessage soapMsg)
            
throws ParserConfigurationException, SAXException, SOAPException,
            IOException 
{
        ByteArrayOutputStream byteArrayOutputStream 
= new ByteArrayOutputStream();
        soapMsg.writeTo(byteArrayOutputStream);
        ByteArrayInputStream bais 
= new ByteArrayInputStream(
                byteArrayOutputStream.toByteArray(), 
0, byteArrayOutputStream
                        .size());

        DocumentBuilderFactory documentBuilderFactory 
= DocumentBuilderFactory
                .newInstance();
        documentBuilderFactory.setNamespaceAware(
true);
        DocumentBuilder documentBuilder 
= documentBuilderFactory
                .newDocumentBuilder();
        Document doc 
= documentBuilder.parse(bais);
        
return doc;
    }

}


  WSClientHandler.java
作为一个抽象类,主要初始化一些相关信息,invoke方法由其子类实现,代码如下:

package com.macrowing.sys.security;

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.apache.axis.handlers.BasicHandler;

public abstract class WSClientHandler extends BasicHandler {

    
private static final long serialVersionUID = -4494231325476939491L;

    
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 abstract void invoke(MessageContext messageContext) throws AxisFault;

    
public void onFault(MessageContext msgContext) {
    }


}


  WSClientRequestHandler.java
实现代码如下:

package com.macrowing.sys.security;

import javax.xml.soap.SOAPMessage;

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.w3c.dom.Document;

public class WSClientRequestHandler extends WSClientHandler {

    
private static final long serialVersionUID = -2966519798377660568L;

    
public void invoke(MessageContext messageContext) throws AxisFault {
        System.out.println(
"1.客户端发出请求");
        
try {
            SOAPMessage soapMessage 
= messageContext.getMessage();
            Document doc 
= MessageConverter
                    .convertSoapMessageToDocument(soapMessage);

            WSSecurityHelper.sign(doc, keyStoreFile, keyStoreType,
                    keyStorePassword, keyAlias, keyEntryPassword);

            WSSecurityHelper.encrypt(doc, trustStoreFile, trustStoreType,
                    trustStorePassword, certAlias);

            soapMessage 
= MessageConverter.convertDocumentToSOAPMessage(doc);
            messageContext.setMessage(soapMessage);
        }
 catch (Exception e) {
            System.err.println(
"在处理响应时发生以下错误: " + e);
            e.printStackTrace();
        }

    }


}


  WSClientResponseHandler.java实现代码如下:

package com.macrowing.sys.security;

import javax.xml.soap.SOAPMessage;

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.w3c.dom.Document;

public class WSClientResponseHandler extends WSClientHandler {

    
private static final long serialVersionUID = -8103853123763763505L;

    
public void invoke(MessageContext messageContext) throws AxisFault {
        System.out.println(
"4.客户端接收响应");
        
try {
            SOAPMessage soapMessage 
= messageContext.getCurrentMessage();
            Document doc 
= MessageConverter
                    .convertSoapMessageToDocument(soapMessage);

            WSSecurityHelper.decrypt(doc, keyStoreFile, keyStoreType,
                    keyStorePassword, keyAlias, keyEntryPassword);

            WSSecurityHelper.verify(doc, trustStoreFile, trustStoreType,
                    trustStorePassword);

            WSSecurityHelper.removeWSSElements(doc);
            soapMessage 
= MessageConverter.convertDocumentToSOAPMessage(doc);
            messageContext.setMessage(soapMessage);
        }
 catch (Exception e) {
            e.printStackTrace();
            System.err.println(
"在处理响应时发生以下错误: " + e);
        }

    }


}


  WSServerHandler.java
同样作为一个抽象类,主要初始化一些相关信息,invoke方法由其子类实现,代码如下:

package com.macrowing.sys.security;

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.apache.axis.handlers.BasicHandler;

public abstract class WSServerHandler extends BasicHandler {

    
private static final long serialVersionUID = -5333475846617822121L;

    
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 onFault(MessageContext msgContext) {
    }


    
/**
     * 初始化,从配置文件server-config.wsdd中读取属性
     
*/

    
public void init() {
        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");
        }

    }


    
public abstract void invoke(MessageContext arg0) throws AxisFault;
    
}


  WSServerRequestHandler.java
实现代码如下:

package com.macrowing.sys.security;

import javax.xml.soap.SOAPMessage;

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.w3c.dom.Document;

public class WSServerRequestHandler extends WSServerHandler {

    
private static final long serialVersionUID = 6462778870280567942L;

    
public void invoke(MessageContext messageContext) throws AxisFault {
        System.out.println(
"2.服务端接收请求");
        
try {
            SOAPMessage msg 
= messageContext.getCurrentMessage();
            Document doc 
= MessageConverter.convertSoapMessageToDocument(msg);

            WSSecurityHelper.decrypt(doc, keyStoreFile, keyStoreType,
                    keyStorePassword, keyAlias, keyEntryPassword);

            WSSecurityHelper.verify(doc, trustStoreFile, trustStoreType,
                    trustStorePassword);

            WSSecurityHelper.removeWSSElements(doc);
            msg 
= MessageConverter.convertDocumentToSOAPMessage(doc);
            messageContext.setMessage(msg);
        }
 catch (Exception e) {
            e.printStackTrace();
            System.err.println(
"在处理响应时发生以下错误: " + e);
        }

    }


}


  WSServerResponseHandler.java实现代码如下:

package com.macrowing.sys.security;

import javax.xml.soap.SOAPMessage;

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.w3c.dom.Document;

public class WSServerResponseHandler extends WSServerHandler {

    
private static final long serialVersionUID = -2376918924804535243L;

    
public void invoke(MessageContext messageContext) throws AxisFault {
        System.out.println(
"3.服务端响应请求");
        
try {
            SOAPMessage soapMessage 
= messageContext.getMessage();
            Document doc 
= MessageConverter
                    .convertSoapMessageToDocument(soapMessage);

            WSSecurityHelper.sign(doc, keyStoreFile, keyStoreType,
                    keyStorePassword, keyAlias, keyEntryPassword);

            WSSecurityHelper.encrypt(doc, trustStoreFile, trustStoreType,
                    trustStorePassword, certAlias);

            soapMessage 
= MessageConverter.convertDocumentToSOAPMessage(doc);
            messageContext.setMessage(soapMessage);
        }
 catch (Exception e) {
            System.err.println(
"在处理响应时发生以下错误: " + e);
            e.printStackTrace();
        }

    }


}


  这样,基于WS-Security规范的SOAP安全程序框架建立好了。为了使用方便,可以把所有的class打包成jar包放在%TOMCAT_HOME%\webapps\axis\WEB-INF\lib目录下。然后,如上文所述,将服务端的Handler发布于server-config.wsdd文件中,如下:

    <handler name="ServerRequestHandler" type="java:com.macrowing.sys.security.WSServerRequestHandler">
        
<parameter name="keyStoreFile" value="server.keystore"/>
        
<parameter name="trustStoreFile" value="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>
    
<handler name="ServerResponseHandler" type="java:com.macrowing.sys.security.WSServerResponseHandler">
        
<parameter name="keyStoreFile" value="server.keystore"/>
        
<parameter name="trustStoreFile" value="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>

  并且不要忘记在需要保障信息安全的Web service中加入:

    <requestFlow>
        
<handler type="soapmonitor"/>
        
<handler type="ServerRequestHandler"/>
    
</requestFlow>
    
<responseFlow>
        
<handler type="soapmonitor"/>
        
<handler type="ServerResponseHandler"/>
    
</responseFlow>


  好了,服务端程序部署完了,编写客户端程序WSClient.java测试:

    public static void main(String[] args) {
        
try {
            String endpointURL 
= "http://localhost:8080/axis/services/Hello";

            WSClientHandler handler 
= new WSClientRequestHandler();
            handler.setInitialization(
"client.keystore""changeit""Client",
                    
"changeit""client.truststore""changeit""serverkey");
            WSClientHandler handlee 
= new WSClientResponseHandler();
            handlee.setInitialization(
"client.keystore""changeit""Client",
                    
"changeit""client.truststore""changeit""serverkey");

            Service svc 
= new Service();
            Call call 
= (Call) svc.createCall();
            call.setClientHandlers(handler, handlee);
            call.setTargetEndpointAddress(
new URL(endpointURL));
            call.setOperationName(
new QName("sayHello"));

            call.invoke(
new Object[] "apos" });

        }
 catch (Exception e) {
            e.printStackTrace();
        }

    }


  启动Tomcat,打开SOAP Monitor,然后运行客户端,OK,你可以看到在monitor上看到需要的效果了。


评论

# re: 基于Axis 1.X的Web Service开发(三)[未登录]  回复  更多评论   

2012-11-07 10:13 by 初学者
受教了,转了,谢谢博主

只有注册用户登录后才能发表评论。


网站导航: