<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 495,comments - 227,trackbacks - 0
    高級(jí)軟件工程師
    2004 年 8 月
    本文將首先簡(jiǎn)單介紹Web 服務(wù)安全性相關(guān)技術(shù)和開(kāi)發(fā)工具,然后介紹了使用WSSecurity工具SOAP消息進(jìn)行簽名和驗(yàn)證的方法;接下來(lái)深入討論了使用現(xiàn)成的WS-Security工具,結(jié)合Handler模型開(kāi)發(fā)一個(gè)axis下實(shí)現(xiàn)WS-Security的通用應(yīng)用框架。

    本文是J2EE Web服務(wù)開(kāi)發(fā)系列文章的第十三篇,本文將首先簡(jiǎn)單介紹Web 服務(wù)安全性相關(guān)技術(shù)和開(kāi)發(fā)工具,然后介紹了使用WSSecurity工具SOAP消息進(jìn)行簽名和驗(yàn)證的方法;接下來(lái)深入討論了使用現(xiàn)成的WS-Security工具,結(jié)合Handler模型開(kāi)發(fā)一個(gè)axis下實(shí)現(xiàn)WS-Security的通用應(yīng)用框架。

    閱讀本文前您需要以下的知識(shí)和工具:

    • Apache axis1.1,并且會(huì)初步使用;
    • Tomcat 5.0.16以上, 并且會(huì)初步使用;
    • SOAP消息(SOAP Message)編程知識(shí);
    • Java安全編程基礎(chǔ)知識(shí);
    • JAX-RPC編程基礎(chǔ)知識(shí);
    • Servlet的開(kāi)發(fā)經(jīng)驗(yàn);
    • Sun提供的JAX-RPC參考實(shí)現(xiàn)(jaxrpc-impl.jar,在J2EESDK1.4或者JWSDP1.4中可找到);
    • 一個(gè)JSSE安全提供者(如ISNetworks);
    • Trust Services Integration Kit,可在http://www.xmltrustcenter.org上獲得。

    本文的參考資料見(jiàn) 參考資料。

    本文的全部代碼在這里 下載。

    Web 服務(wù)安全性相關(guān)技術(shù)和開(kāi)發(fā)工具
    Web 服務(wù)安全性規(guī)范是一套可以幫助 Web 服務(wù)開(kāi)發(fā)者保證 SOAP 消息交換的安全的機(jī)制。WS-Security 特別描述了對(duì)現(xiàn)有的 SOAP 消息傳遞的增強(qiáng),從而通過(guò)對(duì) SOAP 消息應(yīng)用消息完整性、消息機(jī)密性和單消息認(rèn)證提供了保護(hù)級(jí)別。這些基本機(jī)制可以通過(guò)各種方式聯(lián)合,以適應(yīng)構(gòu)建使用多種加密技術(shù)的多種安全性模型。

    圍繞Web服務(wù)的安全,有很多相關(guān)的技術(shù),比如WS-Security,WS-Trace等,另外,還有以下相關(guān)技術(shù):

    • XML Digital Signature(XML數(shù)字簽名)
    • XML Encryption (XML加密)
    • XKMS (XML Key Management Specification)
    • XACML (eXtensible Access Control Markup Language)
    • SAML (Secure Assertion Markup Language)
    • ebXML Message Service Security
    • Identity Management & Liberty Project

    由于本文是一個(gè)實(shí)例性文章,故不對(duì)WS-Security做詳細(xì)的探討,你可以在develperWorks Web 服務(wù)安全專(zhuān)題找到許多相關(guān)資料(見(jiàn)參考資料)。

    Trust Services Integration Kit提供了一個(gè)WS-Security實(shí)現(xiàn)。你可以從http://www.xmltrustcenter.org獲得相關(guān)庫(kù)文件,分別是wssecurity.jar和tsik.jar。wssecurity.jar中包含一個(gè)WSSecurity類(lèi),可以使用它來(lái)對(duì)XML進(jìn)行數(shù)字簽名和驗(yàn)證,加密與解密。

    下面我們使用WS-Security來(lái)對(duì)SOAP消息進(jìn)行數(shù)字簽名,然后再進(jìn)行驗(yàn)證。

    SOAP消息的簽名和驗(yàn)證

    使用WSSecurity對(duì)SOAP消息數(shù)字簽名
    在對(duì)SOAP消息進(jìn)行簽名前,首先生成一個(gè)keystore。keystore包含了進(jìn)行數(shù)字簽名所需要的身份信息。通過(guò)以下批處理腳本來(lái)創(chuàng)建keystore:

    例程1 創(chuàng)建keystore(server.keystore)
    
    set SERVER_DN="CN=hellking-Server, OU=huayuan, O=huayuan, L=BEIJINGC, S=BEIJING, C=CN"
    set KS_PASS=-storepass changeit
    set KS_TYPE=-storetype JKS
    set KEYINFO=-keyalg RSA
    #生成服務(wù)器端keystore。
    keytool -genkey -dname %SERVER_DN% %KS_PASS% %KS_TYPE% -keystore 
    server.keystore %KEYINFO% -keypass changeit
    

    SignAndVerifySoap類(lèi)中包含了一個(gè)對(duì)XML進(jìn)行簽名的方法,它就是sign(),這個(gè)方法將對(duì)SOAP消息進(jìn)行簽名,然后輸出和WS-Security兼容的SOAP消息。下面我們看具體代碼。

    例程2 對(duì)SOAP消息簽名
    
    	package com.hellking.study.webservice;
    import com.verisign.messaging.WSSecurity;
    ...
    public class SignAndVerifySoap {
    	
    	final String KEY_STORE = "server.keystore";
        final String SOTE_PASS = "changeit";
        final String KEY_ALIAS="mykey";
        final String TARGET_FILE="signed.xml";//簽名后的SOAP消息
        final String SOURE_FILE="source.xml";//簽名前的SOAP消息
        final String KEY_TYPE="JKS";
    	
    	/**
    	 *對(duì)xml進(jìn)行簽名
    	 */
    	public void sign()
    	{	
    
    		try
    		{
    			System.out.println("開(kāi)始對(duì)SOAP消息進(jìn)行簽名,使用的密匙庫(kù):" + KEY_STORE + "\n");
    		
    		// 獲得私有key和相關(guān)證書(shū),請(qǐng)參考JAVA安全編程相關(guān)書(shū)籍
    		FileInputStream fileInputStream = new FileInputStream(KEY_STORE);
    		System.out.println(java.security.KeyStore.getDefaultType());
    		java.security.KeyStore store = java.security.KeyStore.getInstance(KEY_TYPE);
    		store.load(fileInputStream, SOTE_PASS.toCharArray());
    		PrivateKey key = (PrivateKey)store.getKey(KEY_ALIAS, SOTE_PASS.toCharArray());
    		X509Certificate certification = (X509Certificate)store.getCertificate(KEY_ALIAS);
    
    		// 讀取XML源文件到文檔中
    		Document source = readFile(SOURE_FILE);
    		SigningKey signingKey = SigningKeyFactory.makeSigningKey(key);
    		KeyInfo keyInfo = new KeyInfo();
    		keyInfo.setCertificate(certification);
    
    		WSSecurity wsSecurity = new WSSecurity();
    
    		wsSecurity.setPreferredNamespace("http://schemas.xmlsoap.org/ws/2003/06/secext");
    		//對(duì)SOAP消息進(jìn)行簽名
    		wsSecurity.sign(source, signingKey, keyInfo);
    		// 保存簽名后的SOAP消息	
    		writeFile(source, new FileOutputStream(TARGET_FILE));
    		System.out.println("把簽名后的文件寫(xiě)入: " + TARGET_FILE + ",請(qǐng)查看結(jié)果!");
    	}
    	catch(Exception e)
    	{
    		e.printStackTrace();
    	}		
    	}
    	

    在執(zhí)行此程序前,請(qǐng)把wssecurity.jar、source.xml和tsik.jar設(shè)置到類(lèi)路徑環(huán)境變量中。簽名前的SOAP為:

    例程3 簽名前的SOAP消息(source.xml)
    
    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope 
    	xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    	xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <soapenv:Body>
      <ns1:getTax soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:ns1="http://hellking.webservices.com/">
       <op1 xsi:type="xsd:double">5000.0</op1>
      </ns1:getTax>
     </soapenv:Body>
    </soapenv:Envelope>
    

    簽名后的SOAP消息如例程4所示。

    例程4 簽名后的SOAP消息(signed.xml)
    
    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <soapenv:Header>
    <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext">
    <wsse:BinarySecurityToken EncodingType="wsse:Base64Binary" 
    ValueType="wsse:X509v3" wsu:Id="wsse-ee805a80-cd95-11d8-9cf9-fd6213c0f8be" 
    xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility">MIICUjCCAbsCBEDB0GIwDQYJKoZIhvcNAQE…VkTkPw==
    </wsse:BinarySecurityToken>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
    <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <ds:Reference URI="#wsse-ee5308f0-cd95-11d8-9cf9-fd6213c0f8be">
    <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>ZjRVnI2g7kcX0h9r4JtiltpYQPA=</ds:DigestValue></ds:Reference>
    <ds:Reference URI="#wsse-ee4e4e00-cd95-11d8-9cf9-fd6213c0f8be">
    <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>moZ0d+8mH1kfNw0VEK39V0Td9EM=</ds:DigestValue>
    </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>fPpYrf0uNP8W2XVVIQNc3OQt2Wn90M/0uJ0dDZTNRR0NxBBBX36wSXt7NfI5Fmh4ru44Wk34EGI7mqMAE5O0
    /wtIlFRJt3zAvA6k3nhgcYj6tn/9kZwwxh1RkFTfTX9xdQ6Xn+P6m+YBm1YEEcTWkJd7XcxdyDEns2kYOhONx1U=
    </ds:SignatureValue>
    <ds:KeyInfo><wsse:SecurityTokenReference>
    <wsse:Reference URI="#wsse-ee805a80-cd95-11d8-9cf9-fd6213c0f8be"/>
    </wsse:SecurityTokenReference>
    </ds:KeyInfo>
    </ds:Signature></wsse:Security>
    <wsu:Timestamp xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility">
    <wsu:Created wsu:Id="wsse-ee4e4e00-cd95-11d8-9cf9-fd6213c0f8be">2004-07-04T08:41:23Z</wsu:Created>
    </wsu:Timestamp></soapenv:Header>
    <soapenv:Body wsu:Id="wsse-ee5308f0-cd95-11d8-9cf9-fd6213c0f8be" 
    	xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility">
      <ns1:getTax soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
      	xmlns:ns1="http://hellking.webservices.com/">
       <op1 xsi:type="xsd:double">5000.0</op1>
      </ns1:getTax>
     </soapenv:Body>
    </soapenv:Envelope>
    

    簽名后的SOAP消息中,頭部包含了簽名信息以及驗(yàn)證SOAP消息所需要的key。<SignedInfo> </SignedInfo> 描述了已簽署的消息內(nèi)容。<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> 指出了簽名算法(Signature Method Algorithm)。這個(gè)算法被用來(lái)將規(guī)范算法的輸出轉(zhuǎn)換成簽名值(Signature Value)。Key Info 元素包含的部分就是數(shù)字證書(shū)本身。

    對(duì)簽名的SOAP消息進(jìn)行驗(yàn)證
    對(duì)SOAP消息進(jìn)行驗(yàn)證就是使用keystore的信息生成TrustVerifier對(duì)象,然后調(diào)用WSSecurity的verify方法進(jìn)行驗(yàn)證。

    例程5 驗(yàn)證簽名后的SOAP消息
    
    	/**
    	 *驗(yàn)證已經(jīng)簽名的SOAP消息
    	 */
    	public void verify()
    	{
    		try
    		{
    			System.out.println("開(kāi)始檢驗(yàn)SOAP消息,使用的密匙庫(kù):" + KEY_STORE + "\n");
    		
    		// 獲得私有key和相關(guān)證書(shū),請(qǐng)參考JAVA安全編程相關(guān)書(shū)籍
    		FileInputStream fileInputStream = new FileInputStream(KEY_STORE);
    
    		java.security.KeyStore store = java.security.KeyStore.getInstance(KEY_TYPE);
    		store.load(fileInputStream, SOTE_PASS.toCharArray());
    	
    		// 讀取XML源文件到文檔中
    		Document source = readFile(TARGET_FILE);
    		org.xmltrustcenter.verifier.TrustVerifier verifier =
                new org.xmltrustcenter.verifier.X509TrustVerifier(store);
    
    		WSSecurity wsSecurity = new WSSecurity();
    
            com.verisign.messaging.MessageValidity[] resa =
         wsSecurity.verify(source,verifier,null,null);
           System.out.println("檢驗(yàn)結(jié)果:");
    	   for (int len = 0; len < resa.length; len++){
          System.out.println("result[" + len + "] = " + (resa[len].isValid()?"驗(yàn)證通過(guò)":"驗(yàn)證不通過(guò)"));
        }
        }
    
        catch(Exception e)
    	{
    		e.printStackTrace();
    	}
    }
    

    執(zhí)行SignAndVerifySoap的verify方法,可以看到類(lèi)似以下的結(jié)果。

    圖1 對(duì)SOAP消息進(jìn)行驗(yàn)證
    圖1 對(duì)SOAP消息進(jìn)行驗(yàn)證

    在AXIS下實(shí)現(xiàn)WS-Security的應(yīng)用框架
    待開(kāi)發(fā)的應(yīng)用開(kāi)發(fā)框架基于Handler實(shí)現(xiàn),將達(dá)到以下目標(biāo):此框架基于JAX-RPC環(huán)境下實(shí)現(xiàn)WS-Security應(yīng)用,它可以部署到任何需要實(shí)現(xiàn)WS-Security的axis環(huán)境下的Web服務(wù)應(yīng)用中,同時(shí)具體的應(yīng)用程序不做任何編碼修改。

    由于此基于Handler實(shí)現(xiàn),我們有必要回顧一下Handler的一些基礎(chǔ)知識(shí)。

    SOAP消息Handler能夠訪問(wèn)代表RPC請(qǐng)求或者響應(yīng)的SOAP消息。在JAX-RPC技術(shù)中,SOAP消息Handler可以部署在服務(wù)端,也可以在客戶(hù)端使用。

    SOAP消息Handler非常像Servlet技術(shù)中的Filter,它們共同的特點(diǎn)是請(qǐng)求發(fā)送到目標(biāo)前,Handler/Filter可以截取這些請(qǐng)求,并對(duì)請(qǐng)求做一些處理,從而達(dá)到一些輔助的功能。多個(gè)Handler可以組成一個(gè)Handler鏈,鏈上的每個(gè)Handler都完成某個(gè)特定的任務(wù)。比如有的Handler進(jìn)行權(quán)限驗(yàn)證,有的Handler進(jìn)行日志處理等。關(guān)于Handler更詳細(xì)的介紹,請(qǐng)參考本系列文章《 J2EE Web服務(wù)開(kāi)發(fā)系列之六: 使用Handler來(lái)增強(qiáng)Web服務(wù)的功能》。

    實(shí)現(xiàn)原理
    圖2是此例子具體實(shí)現(xiàn)原理圖。

    圖2 Handler結(jié)合WSSecurity實(shí)現(xiàn)Web服務(wù)安全的工作原理
    圖2 Handler結(jié)合WSSecurity實(shí)現(xiàn)Web服務(wù)安全的工作原理

    處理流程如下:

    1、 客戶(hù)端(WSSClient)發(fā)出調(diào)用Web服務(wù)請(qǐng)求;

    2、 客戶(hù)端Handler(WSSecurityClientHandler)截獲請(qǐng)求的SOAP消息;

    3、 客戶(hù)端Handler對(duì)截獲的SOAP消息進(jìn)行數(shù)字簽名(使用client.keystore作為簽名依據(jù));

    4、 客戶(hù)端Handler對(duì)簽名后的SOAP消息進(jìn)行加密(使用RSA算法加密);

    5、 被加密的SOAP消息通過(guò)互聯(lián)網(wǎng)傳送到目標(biāo)Web服務(wù)端口;

    6、 服務(wù)器端Handler(WSSecurityServerHandler)截獲加密的SOAP消息;

    7、 服務(wù)器端Handler對(duì)加密的SOAP消息進(jìn)行解密;

    8、 服務(wù)器端Handler對(duì)SOAP消息進(jìn)行身份驗(yàn)證(server.truststore包含了所信任的身份信息),如果驗(yàn)證不通過(guò),將拋出異常;

    9、 服務(wù)器端Handler刪除被解密后的SOAP消息中與WS-Security相關(guān)的元素;

    10、 解密后的原始SOAP消息被發(fā)送到目標(biāo)Web服務(wù)端口(如TaxService);

    11、 目標(biāo)Web服務(wù)對(duì)Web服務(wù)請(qǐng)求進(jìn)行處理,然后返回響應(yīng)的SOAP消息;

    12、 服務(wù)器端Handler截獲響應(yīng)的SOAP消息;

    13、 服務(wù)器端Handler對(duì)截獲的SOAP消息進(jìn)行數(shù)字簽名(使用server.keystore作為簽名依據(jù));

    14、 服務(wù)器端Handler對(duì)簽名后的SOAP消息進(jìn)行加密(使用RSA算法加密);

    15、 被加密的SOAP消息通過(guò)互聯(lián)網(wǎng)傳送到目客戶(hù)端;

    16、 客戶(hù)端Handler截獲加密的SOAP消息;

    17、 客戶(hù)端Handler對(duì)加密的SOAP消息進(jìn)行解密;

    18、 客戶(hù)端Handler對(duì)SOAP消息進(jìn)行身份驗(yàn)證(client.truststore包含了所信任的身份信息),如果驗(yàn)證不通過(guò),將拋出異常;

    19、 客戶(hù)端Handler刪除被解密后的SOAP消息中與WS-Security相關(guān)的元素;

    20、 被解密后的SOAP消息發(fā)送到目標(biāo)客戶(hù)端,客戶(hù)端輸出調(diào)用結(jié)果。

    從上面可以看出,在一個(gè)SOAP調(diào)用回合中,要對(duì)SOAP消息進(jìn)行四次處理?;旧隙际?簽名'加密'解密'驗(yàn)證"的過(guò)程。

    創(chuàng)建相關(guān)密匙庫(kù)
    客戶(hù)端和服務(wù)端都有相關(guān)的密匙庫(kù),其中:

    • client.keystore:客戶(hù)端自身的身份信息;
    • client.truststore:客戶(hù)端所信任的身份信息,在此例中也就是包含了服務(wù)器的身份信息;
    • server.keystore:服務(wù)器自身的身份信息;
    • server.truststore:服務(wù)器所信任的身份信息(即客戶(hù)端身份信息)。

    你可以使用以下的批處理腳本創(chuàng)建上面四個(gè)密匙庫(kù)。

    例程6 創(chuàng)建相關(guān)密匙庫(kù)(gen-cer-store.bat)
    
    set SERVER_DN="CN=hellking-Server, OU=huayuan, O=huayuan, L=BEIJINGC, S=BEIJING, C=CN"
    set CLIENT_DN="CN=hellking-Client, OU=tsinghua, O=tsinghua, L=BEIJING, S=BEIJING, C=CN"
    set KS_PASS=-storepass changeit
    set KEYINFO=-keyalg RSA
    #生成server.keystore。
    keytool -genkey -dname %SERVER_DN% %KS_PASS% -keystore server.keystore %KEYINFO% -keypass changeit
    #從server.keystore導(dǎo)出數(shù)字證書(shū)。
    keytool -export -file test_axis.cer %KS_PASS% -keystore server.keystore
    #從服務(wù)器的數(shù)字證書(shū)導(dǎo)出到客戶(hù)端信任的truststore中。
    keytool -import -file test_axis.cer %KS_PASS% -keystore client.truststore -alias serverkey -noprompt
    
    #生成client.keystore。
    keytool -genkey -dname %CLIENT_DN% %KS_PASS% -keystore client.keystore %KEYINFO% -keypass changeit
    #從client.keystore導(dǎo)出數(shù)字證書(shū)。
    keytool -export -file test_axis.cer %KS_PASS% -keystore client.keystore
    #從客戶(hù)端的數(shù)字證書(shū)導(dǎo)出到服務(wù)器信任的truststore中。
    keytool -import -file test_axis.cer %KS_PASS% -keystore server.truststore -alias clientkey -noprompt
    
    #end
    

    簽名、加密、解密、身份驗(yàn)證的實(shí)現(xiàn)
    對(duì)SOAP消息的簽名、加密、解密、身份驗(yàn)證都放在一個(gè)名為WSSHelper的類(lèi)中進(jìn)行。

    例程7 簽名、加密、解密、身份驗(yàn)證功能的實(shí)現(xiàn)――WSSHelper.java
    
    package com.hellking.study.webservice;
    import com.verisign.messaging.WSSecurity;
    ...
    public class WSSHelper {
    	static String PROVIDER="ISNetworks";//JSSE安全提供者。	
       //添加JSSE安全提供者,你也可以使用其它安全提供者。只要支持DESede算法。
    	static
    	{
    	java.security.Security.addProvider(new com.isnetworks.provider.jce.ISNetworksProvider());
        }
        /**
         *對(duì)XML文檔進(jìn)行數(shù)字簽名。
         */
    	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();
    		wSSecurity.sign(doc, sk, ki);//簽名。
    	}
        /**
         *對(duì)XML文檔進(jìn)行身份驗(yàn)證。
         */
    	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;
    	}
       /**
        *對(duì)XML文檔進(jìn)行加密。必須有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();
    	}
    	}
        /**
         *對(duì)文檔進(jìn)行解密。
         */
    	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相關(guān)的元素。
    	}		
    }
    

    WSSHelper類(lèi)中使用了ISNetworks安全提供者,ISNetworks實(shí)現(xiàn)了RSA加密、解密算法。當(dāng)然,你也可以使用其它的安全提供者,并且可以使用不同的加密算法??梢詮木W(wǎng)絡(luò)上下載ISNetworks相關(guān)包。

    WSSHelper中包含了一個(gè)WsUtils類(lèi),它的功能就是從加密后的SOAP消息中刪除一些WS-Security元素,刪除這些元素后的SOAP消息才能被最終的客戶(hù)端或者Web服務(wù)端處理。

    服務(wù)器端Handler開(kāi)發(fā)
    當(dāng)請(qǐng)求到達(dá)后,服務(wù)端Handler調(diào)用handleRequest方法,執(zhí)行如下過(guò)程:對(duì)請(qǐng)求SOAP消息解密'身份驗(yàn)證'刪除WSS元素'把Document轉(zhuǎn)換成SOAP消息。 Web服務(wù)端點(diǎn)對(duì)請(qǐng)求做出響應(yīng)后,將調(diào)用handleResponse方法,執(zhí)行如下過(guò)程:對(duì)響應(yīng)的SOAP消息進(jìn)行數(shù)字簽名'加密'把Document轉(zhuǎn)換成SOAP消息。

    例程8 服務(wù)器端Handler(WSSecurityServerHandler.java)
    
    package com.hellking.study.webservice;
    ...
    
    //服務(wù)器端Handler
    public class WSSecurityServerHandler implements Handler 
    {
    	 	//密匙庫(kù)相關(guān)信息
    private String keyStoreFile = null;
    		private String keyStoreType = "JKS";
    		。。。
    		
    		public WSSecurityServerHandler()
    		{
    			System.out.println("服務(wù)端Handler:構(gòu)造方法");
    		}
    	    /**
    	     *處理請(qǐng)求
    	     *流程:解密-->身份驗(yàn)證-->刪除WSS元素'把Document轉(zhuǎn)換成SOAP消息。
    	     */	     
    	    public boolean handleRequest(MessageContext messageContext) {
    	 	
    	        	System.out.println("開(kāi)始處理請(qǐng)求。。。");
        	if (messageContext instanceof SOAPMessageContext){
    			try {
    	SOAPMessageContext soapMessageContext = (SOAPMessageContext)messageContext;
    				SOAPMessage soapMessage = soapMessageContext.getMessage();
    				soapMessage.writeTo(System.out);
    	Document doc = MessageConveter.convertSoapMessageToDocument(soapMessage);
                        //解密
    				WSSHelper.decrypt(doc, keyStoreFile, keyStoreType,
    						keyStorePassword, keyAlias, keyEntryPassword);
                         //身份驗(yàn)證
    				WSSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);
    				//刪除WSS元素
    WSSHelper.removeWSSElements(doc);
    
    				soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
    				soapMessageContext.setMessage(soapMessage);
    			} catch (Exception e){
    
    				System.err.println("在處理請(qǐng)求時(shí)發(fā)生了異常: " + e);
    				e.printStackTrace();
    				return false;
    			}
    		} else {
    			System.out.println("MessageContext是以下類(lèi)的實(shí)例: " + messageContext.getClass());
    		}
    		 System.out.println("處理請(qǐng)求完畢!");
            return true;
        }
        
        /**
         *處理響應(yīng)
         *流程:數(shù)字簽名-->加密-->把Document轉(zhuǎn)換成SOAP消息。
         */
         public boolean handleResponse(MessageContext messageContext) {
            	System.out.println("開(kāi)始處理Web服務(wù)響應(yīng)。。。");
            if (messageContext instanceof SOAPMessageContext){
    			try {
    		SOAPMessageContext soapMessageContext = (SOAPMessageContext)messageContext;
    				SOAPMessage soapMessage = soapMessageContext.getMessage();
    		Document doc = MessageConveter.convertSoapMessageToDocument(soapMessage);
    				WSSHelper.sign(doc, keyStoreFile, keyStoreType,
    						keyStorePassword, keyAlias, keyEntryPassword);
    				WSSHelper.encrypt(doc, trustStoreFile, trustStoreType,
    						trustStorePassword, certAlias);
    
    				soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
    				soapMessageContext.setMessage(soapMessage);
    			} catch (Exception e){
    				System.err.println("在處理響應(yīng)時(shí)發(fā)生以下錯(cuò)誤: " + e);
    				e.printStackTrace();
    				return false;
    			}
    		}
    	
    		System.out.println("處理響應(yīng)完畢!");
            return true;
        }
        /**
         *初始化,主要是初始化一些相關(guān)參數(shù)。
         */
         public void init(HandlerInfo config) {
    		System.out.println("WSSecurityServerHandler初始化");
    		Object param = "";
    		Map configs = config.getHandlerConfig();
    		keyStoreFile = (String)configs.get("keyStoreFile");
    		trustStoreFile = (String)configs.get("trustStoreFile");
    		…//其它參數(shù)初始化	
        }
    	…
    }
    

    客戶(hù)端Handler開(kāi)發(fā)
    客戶(hù)端Handler可以是任何JAX-RPC兼容的Handler處理器。比如AXIS Handler實(shí)現(xiàn)或者SUN 提供的JAX-RPC Handler參考實(shí)現(xiàn)。這里使用后者來(lái)作為客戶(hù)端Handler處理器。

    客戶(hù)端Handler和服務(wù)器端Handler原理一樣,但處理過(guò)程完全相反。

    例程9 客戶(hù)端Handler(WSSecurityClientHandler.java)
    
    package com.hellking.study.webservice;
    …
    
    //客戶(hù)端Handler
    public class WSSecurityClientHandler implements Handler 
    {       
           //密匙庫(kù)相關(guān)信息   
    	    ...
    		
    	    /**
    	     *處理請(qǐng)求
    	     *流程:數(shù)字簽名-->加密-->把Document轉(zhuǎn)換成SOAP消息。
    	     */	     
    	    public boolean handleRequest(MessageContext messageContext) {
    	 	
    	        	System.out.println("開(kāi)始處理請(qǐng)求。。。");
               …
    		       	WSSHelper.sign(doc, keyStoreFile, keyStoreType,
    				keyStorePassword, keyAlias, keyEntryPassword);
    			    WSSHelper.encrypt(doc, trustStoreFile, trustStoreType,
    trustStorePassword, certAlias);
    				soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
    				soapMessageContext.setMessage(soapMessage);
    			…
    		 System.out.println("處理請(qǐng)求完畢!");
            return true;
        }
        
        /**
         *處理響應(yīng)
         *流程:解密-->身份驗(yàn)證-->刪除WSS元素'把Document轉(zhuǎn)換成SOAP消息。
         */
         public boolean handleResponse(MessageContext messageContext) {
    
            	System.out.println("開(kāi)始處理Web服務(wù)響應(yīng)。。。");
           …
    			    WSSHelper.decrypt(doc, keyStoreFile, keyStoreType,
    						keyStorePassword, keyAlias, keyEntryPassword);				
    				
    				WSSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);
    				WSSHelper.removeWSSElements(doc);
    
    				
    				soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
    								System.out.println("the final message is:");
    								soapMessage.writeTo(System.out);
    				soapMessageContext.setMessage(soapMessage);
    			…		
    			System.out.println("處理響應(yīng)完畢!");
            return true;
        }
        /**
         *初始化,主要是初始化一些相關(guān)參數(shù)。
         */
         public void init(HandlerInfo config) {
    		…
        }
    	…
    }
    

    部署服務(wù)器端Handler
    為了使用Handler,需要在Web服務(wù)部署描述符中指定使用此Handler。Handler包含的初始化參數(shù)也在此描述,如例程10所示。

    例程10 服務(wù)器端Handler部署代碼
    
      <service name="PersonalTaxServicePort" provider="java:RPC">
      <parameter name="allowedMethods" value="*"/>
      <parameter name="className" value="/blog/com.hellking.study.webservice.PersonalTaxService"/>
      <parameter name="wsdlTargetNamespace" value="http://hellking.webservices.com/"/>
      <parameter name="wsdlServiceElement" value="PersonalTaxService"/>
      <parameter name="wsdlServicePort" value="PersonalTaxServicePort"/>
      <parameter name="wsdlPortType" value="PersonalTaxService"/>
      <requestFlow>
       <handler type="java:org.apache.axis.handlers.JAXRPCHandler">
        <parameter name="scope" value="session"/>
        <parameter name="className"
                value="/blog/com.hellking.study.webservice.WSSecurityServerHandler"/>
        <parameter name="keyStoreFile"
                value="/blog/K:\\jakarta-tomcat-5.0.16\\server.keystore"/>
        <parameter name="trustStoreFile"
                value="/blog/K:\\jakarta-tomcat-5.0.16\\server.truststore"/>
        <parameter name="certAlias" value="clientkey"/>
       </handler>
      </requestFlow>
      <responseFlow>
       <handler type="java:org.apache.axis.handlers.JAXRPCHandler">
        <parameter name="scope" value="session"/>
        <parameter name="className"
                value="/blog/com.hellking.study.webservice.WSSecurityServerHandler"/>
        <parameter name="keyStoreFile"
                value="/blog/K:\\jakarta-tomcat-5.0.16\\server.keystore"/>
    
        <parameter name="trustStoreFile"
                value="/blog/K:\\jakarta-tomcat-5.0.16\\server.truststore"/>
        <parameter name="certAlias" value="clientkey"/>
       </handler>
      </responseFlow>
      </service>
      

    requestFlow表示W(wǎng)eb服務(wù)PersonalTaxServicePort的請(qǐng)求處理Handler鏈。這里只有一個(gè)Handler,就是WSSecurityServerHandler。當(dāng)Web服務(wù)請(qǐng)求到達(dá)PersonalTaxServicePort時(shí),WSSecurityServerHandler的handleRequest方法將被自動(dòng)調(diào)用。

    注意:部署時(shí),請(qǐng)改變Handler相關(guān)參數(shù)以和目標(biāo)的Web服務(wù)一致,比如trustStoreFile的路徑等。

    調(diào)用測(cè)試
    這里采用代理的方式來(lái)調(diào)用Web服務(wù),先編寫(xiě)一個(gè)Web服務(wù)接口。

    例程11 TaxServiceInterface
    
    package com.hellking.study.webservice;
    …
    /**
     *個(gè)人所得稅Web服務(wù)。
     */
    public interface TaxServiceInterface extends Remote
    {
    	public double getTax(double salary)throws java.rmi.RemoteException;
    }
    

    WSSClient客戶(hù)端程序是通過(guò)代理的方式來(lái)訪問(wèn)Web服務(wù)的。由于要使用Handler,所以在訪問(wèn)前通過(guò)registerHandlers()方法注冊(cè)了WSSecurityClientHandler,并且初始化了WSSecurityClientHandler的相關(guān)參數(shù)。當(dāng)然,JAX-RPC"參考實(shí)現(xiàn)"還支持在Web服務(wù)客戶(hù)端配置文件中描述Handler信息,這樣就不需要在客戶(hù)端代碼中對(duì)Handler進(jìn)行注冊(cè)了,你可以參考相關(guān)文檔。

    例程12 測(cè)試客戶(hù)端程序(WSSClient)
    
    package com.hellking.study.webservice;
    
    ...
    /**
     *調(diào)用需要驗(yàn)證的Web服務(wù)
     */
    public class WSSClient
    {
    	static	final double salary=5000;
        public static void main(String [] args)
        {
            try {
            	//服務(wù)端的url,需要根據(jù)情況更改。            
                String endpointURL = "http://localhost:8080/axis/services/PersonalTaxServicePort";
                String wsdlURL=endpointURL+"?wsdl";
                java.net.URL targetURL= new java.net.url(/blog/wsdlURL);
                String nameSpaceUri = "http://hellking.webservices.com/";
                String svcName = "PersonalTaxService";		
    		    String portName = "PersonalTaxServicePort";
    
                ServiceFactory svcFactory = ServiceFactory.newInstance();
    
                Service svc = svcFactory.createService(targetURL, new QName(nameSpaceUri, svcName));
                //cfg表示客戶(hù)端的配置信息。
                java.util.HashMap cfg = new java.util.HashMap();
    			
    			cfg.put("keyStoreFile", "client.keystore");			
    			cfg.put("trustStoreFile", "client.truststore");			
    			cfg.put("certAlias", "changeit");
    			
    		    Class hdlrClass = com.hellking.study.webservice.WSSecurityClientHandler.class;
    			
    			java.util.List list = svc.getHandlerRegistry().
    			
    			               getHandlerChain(new QName(nameSpaceUri, portName));
    			
    			list.add(new javax.xml.rpc.handler.HandlerInfo(hdlrClass, cfg, null));
    			
    		     registerHandlers (svc);
    			
    			 TaxServiceInterface myProxy = 
                   ( TaxServiceInterface) svc.getPort(new QName(nameSpaceUri, portName), 
                     TaxServiceInterface.class);       
                  double ret=myProxy.getTax(5000);
    			System.out.println("使用HTTP協(xié)議來(lái)作為Web服務(wù)的傳輸協(xié)議!");			
                System.out.println("已經(jīng)成功調(diào)用。請(qǐng)參看服務(wù)端的輸出!");
                System.out.println("輸入工資"+salary+"元,應(yīng)交個(gè)人所得稅:"+ret);
            } catch (Exception e) {
            	e.printStackTrace();
            }
        }
        //注冊(cè)Handler
        private static void registerHandlers ( Service service ) 
    		throws javax.xml.rpc.ServiceException {
    		
    		java.util.HashMap cfg = new java.util.HashMap();
    			
    			cfg.put("keyStoreFile", "client.keystore");			
    			cfg.put("trustStoreFile", "client.truststore");			
    			cfg.put("certAlias", "changeit");
    			
    		/*
    		* 封裝客戶(hù)端Handler到HandlerInfo 中,然后添加到Handler鏈中。
    		*/ 
    	javax.xml.rpc.handler.HandlerInfo info = new javax.xml.rpc.handler.HandlerInfo
    	(com.hellking.study.webservice.WSSecurityClientHandler.class, cfg, null );
    		java.util.ArrayList handlerList = new java.util.ArrayList();
    		handlerList.add(info);
    		
    		/*
    		* 獲得Handler注冊(cè)
    		*/
    		javax.xml.rpc.handler.HandlerRegistry handlerRegistry = service.getHandlerRegistry();
    		
    		/*
    		* 把Handler添加到所有的port中。
    		*/
    		java.util.Iterator portIterator = service.getPorts();
    		while ( portIterator.hasNext()) {
    			Object obj=portIterator.next();
    		QName portName = (QName) obj;
    		handlerRegistry.setHandlerChain(portName, handlerList);
    		}
        }
        
    }
    

    注意:由于客戶(hù)端使用了SUN公司提供的"JAX-RPC參考實(shí)現(xiàn)",所以必須把jaxrpc-impl.jar包設(shè)置在CLASSPATH環(huán)境變量中,并且不要把a(bǔ)xis.jar設(shè)置在客戶(hù)端CLASSPATH環(huán)境變量,否則會(huì)出現(xiàn)ClassCastException異常。這是因?yàn)閍xis也是JAX-RPC的實(shí)現(xiàn),如果它在CLASSPATH環(huán)境變量中,當(dāng)調(diào)用:

    ServiceFactory svcFactory = ServiceFactory.newInstance()方法時(shí),就可能初始化一個(gè)axis的ServiceFactory 實(shí)現(xiàn)。

    本文源代碼中client目錄下wss-client.bat文件包含了執(zhí)行WSSClient腳本,修改了部分環(huán)境變量參數(shù)后,才能執(zhí)行。

    總結(jié)
    本文和上一篇文章介紹了幾種不同實(shí)現(xiàn)Web服務(wù)安全的方法,你可以根據(jù)具體應(yīng)用對(duì)安全的要求級(jí)別采用不同的方式。對(duì)于安全級(jí)別要求不高的應(yīng)用,可以采用在Web服務(wù)器上使用基本認(rèn)證、使用Axis的Handler或者使用Servlet過(guò)濾器來(lái)實(shí)現(xiàn)訪問(wèn)控制;對(duì)于安全要求高的應(yīng)用,可以采用本篇介紹開(kāi)發(fā)的"axis下實(shí)現(xiàn)WS-Security的通用應(yīng)用框架"來(lái)實(shí)現(xiàn)安全性。

    posted on 2006-08-04 20:08 SIMONE 閱讀(1124) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): AXIS
    主站蜘蛛池模板: 女人被免费视频网站| 亚洲国产精品一区二区九九| 免费真实播放国产乱子伦| 亚洲国产精品福利片在线观看 | 亚洲第一区在线观看| 久久久无码精品亚洲日韩蜜桃| 亚洲色大成网站www尤物| 皇色在线免费视频| 欧美好看的免费电影在线观看| av在线亚洲欧洲日产一区二区| 亚洲精品在线视频观看| 青草青草视频2免费观看| 91免费人成网站在线观看18| 亚洲国产91精品无码专区| 亚洲国产精品久久丫| 精品一区二区三区免费观看| 成人毛片视频免费网站观看| 人人狠狠综合久久亚洲88| 亚洲av成人一区二区三区观看在线| 国产99视频精品免费专区| 国产v片免费播放| 亚洲黄色网址在线观看| 狼色精品人妻在线视频免费| www.999精品视频观看免费| 亚洲成A人片在线观看无码不卡| 久久精品熟女亚洲av麻豆| 日韩精品免费一级视频| 亚洲日韩小电影在线观看| 亚洲av日韩综合一区二区三区| 91九色老熟女免费资源站| 国产午夜亚洲精品午夜鲁丝片| 国产偷国产偷亚洲清高APP| 999国内精品永久免费视频| 日韩va亚洲va欧洲va国产| 香蕉视频免费在线播放| 成年在线网站免费观看无广告| 亚洲视频精品在线观看| 在线看片免费人成视频播| 亚洲国产精品成人AV无码久久综合影院| 亚洲综合成人婷婷五月网址| 99久久久国产精品免费牛牛|