程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使用Java驗證Lotus Forms XML數字簽名

使用Java驗證Lotus Forms XML數字簽名

編輯:關於JAVA

Lotus Forms XML 數字簽名簡介

本文關注在不依賴 Lotus Forms API 的情況下,使用 JSR 105 API 對簽名的 Lotus Forms 文檔進行驗證(可下載源 代碼,見 下載 小節)。這種方法簡化了使用標准 Java™ API 調用和來自 其他供應商的 XML 簽名實現,將從其他供應商購買的產品與 Lotus Forms 集成 的過程。

對於本文而言,表單是指包含使用 Extensible Forms Description Language (XFDL) 描述的標記的 XML 文檔,Extensible Forms Description Language (XFDL) 是用於描述 Lotus Forms 文檔的、使用標准 XML 格式的詞匯表。在 XFDL 表單內,用戶界面控件稱為項,項使用 XML 元素和屬性 編碼。項通常被綁定到 XForms 實例中的數據,並且被組織為一個或多個可視頁 面。

XML 簽名被包含在 XFDL 文檔內,因此它們是被封裝的簽名。根據 Model-View-Controller (MVC) 概念,一個 XML 數字簽名屬於 XForms 實例中的 數據模型的一部分,因為數字簽名的生成是一種信息創建行為,由具有私有密匙 的用戶完成。生成簽名後,對 XFDL 文檔的任何修改都將造成簽名驗證失敗。XML 簽名還可以有選擇地忽略被簽名文檔的各部分,這樣多步驟工作流就可以對這些 被忽略的部分執行操作,並且不會影響到數字簽名的有效性。這種安排的兩個優 點就是允許對表單使用多步驟簽名,以及簽名之間可以彼此重疊。

清單 1 展示了 XForms 模型中的 XML 簽名的例子。注意 xforms、ds 和 dsxp 的名稱 空間 URI 在表單的 <XFDL> 文檔元素中定義,這裡並沒有顯示。完整的表 單示例可以在 下載 小節找到。


清單 1. 包含未進行簽名的 XML 簽名的 XForms 實例
<xforms:model>
  <xforms:instance xmlns="" id="Generated">
   <data>
   <page1>
    <customer>
     <firstname1>John</firstname1>
      <middlename1>M</middlename1>
      <lastname1>Smith</lastname1>
      <ssn1>123456789</ssn1>
      <streetaddress1>1000 Main Street</streetaddress1>
      <city1>La</city1>
      <state>California</state>
      <zipcode>10080</zipcode>
      <gender1>M</gender1>
      <dateofbirth1>1980-01-02</dateofbirth1>
      <workphone1>1001234567</workphone1>
      <homephone1>1001236789</homephone1>
     </customer>
    <signature1>
      <ds:Signature>
      <ds:SignedInfo>
       <ds:CanonicalizationMethod  Algorithm="http://www.w3.org/TR/2001/ 
      REC-xml-c14n- 20010315"></ds:CanonicalizationMethod>
       <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig# 
      rsa-sha1"></ds:SignatureMethod>
       <ds:Reference>
       <ds:Transforms>
       <ds:Transform Algorithm="http://www.w3.org/2002/06/  
        xmldsig-filter2">
          <dsxp:XPath Filter="subtract">/xfdl:XFDL/xfdl:globalpage/ 
         xfdl:global/xfdl:xformsmodels/xforms:model[1]/ 
         xforms:instance [@id="Generated"]/data/page1/signature1/ 
          ds:Signature 
         </dsxp:XPath>
          </ds:Transform>
          </ds:Transforms>
         <ds:DigestMethod  Algorithm="http://www.w3.org/2000/09/xmldsig# 
           sha1"></ds:DigestMethod>
           <ds:DigestValue></ds:DigestValue>
          </ds:Reference>
         <ds:Reference  URI="">
          <ds:Transforms>
            <ds:Transform Algorithm="http://www.w3.org/2002/06/ 
            xmldsig-filter2">
              <dsxp:XPath Filter="intersect">here()/ancestor::
             ds:Signature[1]/ds:Object [sigmeta:metadata]</dsxp:XPath>
             </ds:Transform>
        </ds:Transforms>
        <ds:DigestMethod  Algorithm="http://www.w3.org/2000/09/xmldsig# 
         sha1"></ds:DigestMethod>
         <ds:DigestValue></ds:DigestValue>
        </ds:Reference>
       </ds:SignedInfo>
        <ds:SignatureValue></ds:SignatureValue>
        <ds:Object>
         <sigmeta:metadata>
          <sigmeta:timestamp>
           <sigmeta:signtime></sigmeta:signtime>
           <sigmeta:dst></sigmeta:dst>
           <sigmeta:date></sigmeta:date>
          </sigmeta:timestamp>
         </sigmeta:metadata>
       </ds:Object>
       </ds:Signature>
     </signature1>
    </page1>
  </data>
  </xforms:instance>
</xforms:model>

XML 簽名包含一個表示簽名內容的 Reference 元素。Reference 不具有 URI 屬性,在 XFDL 文檔中這表示整個文檔都應當被簽名隱藏。Reference 然後包含 一個 Transform,可以去掉生成的 XML 簽名。這一步是必需的,因為 XML 簽名 被封裝在已簽名的 XFDL 文檔的內部。當所引用的資源(XFDL 文檔)的數字指紋 被計算後,Reference 的 DigestValue 元素將為空。計算結果隨後被存儲到 DigestValue 中,這將修改 XFDL 文檔。然而,實際上沒有發生修改(從 XML 文 檔中去除 XML 簽名),這就是我們從對其計算摘要的內容中去掉已封裝簽名的原 因。

在清單 1 中,SignedInfo 中還有一個 Reference。XML 簽名可以對創建者所 需要的任意數量的資源進行簽名。在本例中,我們使用它對生成的 XML 簽名的額 外元數據進行簽名。例如,這一特性可用於在生成的 XML 簽名中包含一個簡單的 XAdES 時間戳。

圖 1. 樣例簽名表單

驗證簽名表單

查看應用程序(比如 IBM Lotus Forms Viewer)或程序可以使用一個私有密 匙對表單進行簽名。下載 小節提供了一個未進行簽名的樣例表單。建議用戶下載 Lotus Forms Viewer 來對表單進行簽名並理解其工作原理。IBM Lotus Forms Server 還使用戶能夠對表單進行數字簽名,不需要下載和安裝 Lotus Forms Viewer。

除 Lotus Forms API 以外,還可以使用標准的 Java XML Digital Signature API(在 JSR 105 中定義)對簽名表單進行交叉驗證,這個 API 也作為 Java 6 的一部分包含在其中。

要斷言某個表單沒有被篡改,必須對表單中的所有簽名進行驗證。這種驗證可 以通過查找所有包含 Signature 元素和非空 SignatureValue 元素的 XForms 實 例來完成。對於此類 XForms 實例中的每一個,將針對驗證創建一個單獨的、具 有合適名稱空間的實例文檔。出於性能考慮,可以使用用於 XML 的流 API 來拉 取 XForms 實例並放入到一個新的 Document Object Model (DOM) 文檔中。

准備好 DOM 實例文檔後,從要進行驗證的 Signature DOM 節點中創建一個 DOMValidateContext。還需要用一個 KeySelector 從 KeyInfo 部分的簽名證書 中提取公共密匙。DOMValidateContext 使用前面小節中的定制 URI dereferencer 來執行 XML 標准化(canonization)。解組 DOMValidateContext 將創建一個 XMLSignature 對象,其 validate() 方法可以返回表示簽名是否有 效的信息。

對所有 XForms 實例中的所有簽名進行驗證後,將收集最終生成的結果,表示 完整表單的驗證狀態。

處理 XForms 數據模型

如前一小節所述,Lotus Forms 中的 XML 簽名是一個封裝的簽名,包含 XForms 實例的標記。要准確地驗證此類簽名,必須為 XForms 實例數據構建 DOM 。W3C XForms 規范(http://www.w3.org/TR/xforms/#structure-model- instance)要求將實例數據提取到一個單獨的 DOM 文檔中,並且如果實例數據被 內聯到文檔內部,那麼必須從 XForms 實例元素及其祖先節點繼承相應的名稱空 間。所有對數據的操作應當在單獨的 DOM 文檔中完成。

這種方法意味著要創建文檔,必須從包含 XML 簽名的 xform:instance 內容 中克隆 DOM 子樹。新的文檔應當包含注釋和 PI 節點,以及來自 xforms:instance 的惟一子元素。對於與清單 1 相對應的已簽名表單,必須使用 從所有祖先節點繼承的名稱空間創建一個新文檔,其中包含 <data> 文檔 元素。這個文檔被稱為實例文檔,而源文檔被稱為表單文檔。

清單 2. 提取的文檔元素的名稱空間

<data xmlns=""  xmlns:c14n="http://www.w3.org/2001/10/xml-exc-c14n#"
  xmlns:custom="http://www.ibm.com/xmlns/prod/XFDL/Custom"
  xmlns:designer="http://www.ibm.com/xmlns/prod/workplace/forms/designer/ 2.6"
  xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
  xmlns:dsxp="http://www.w3.org/2002/06/xmldsig-filter2"
  xmlns:ev="http://www.w3.org/2001/xml-events"
  xmlns:sigmeta="http://www.ibm.com/xmlns/prod/forms/signature/metadata/1 .0"
  xmlns:xfdl="http://www.ibm.com/xmlns/prod/XFDL/7.6"
  xmlns:xforms="http://www.w3.org/2002/xforms"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
… 
</data>

由於父節點的名稱空間前綴可以被一個子節點覆蓋,比如默認名稱空間,因此 如果名稱空間已被復制到目標,那麼就不會再次復制該名稱空間。這種方法遵守 名稱空間覆蓋規則,因此只有距離最近的名稱空間定義是有效的。

清單 3. 將名稱空間從源節點的所有祖先復制到目標節點的代碼

private void cloneNamespaces(Element src, Element target)  {
   HashMap copiedNamespaces = new HashMap();

   Node node = src;
   while (node != null && node.getNodeType() ==  Node.ELEMENT_NODE) {
     NamedNodeMap attributes = node.getAttributes();
     for (int i = 0; i < attributes.getLength(); i++)  {
       Attr attr = (Attr) attributes.item(i);
       String namespaceUri = attr.getNamespaceURI();
       if (!XML_NS_URI.equals(namespaceUri)) {
         // a normal attribute, don't copy it 
         continue;
       }

       String nodeValue = attr.getNodeValue();
       String localName = attr.getLocalName();
       String prefix = attr.getPrefix();
       String qualifiedName = (prefix == null) ?  localName : prefix
           + ":" + localName; //$NON-NLS-1$

       // don't overwrite namespace already copied.
       if (copiedNamespaces.containsKey(qualifiedName)) {
         continue;
       }

       Document factory = target.getOwnerDocument();
       Attr newAttr = factory.createAttributeNS (namespaceUri,
           qualifiedName);
       newAttr.setNodeValue(nodeValue);
       target.setAttributeNodeNS(newAttr);

       copiedNamespaces.put(qualifiedName, nodeValue);
     }

     node = node.getParentNode();
   }
}

定制 URI dereferencer

由於 XForms 處理模型要求將包含簽名的實例數據從源表單文檔(本例中為 XFDL 文檔)中單獨實例化,因此帶有 URI="" 的引用(相同的文檔引用)表示對 實例文檔的開始部分的引用,而不是完整的 XFDL 文檔。我們希望能夠對整個 XFDL 文檔進行簽名,而不是僅僅是數據,從而在簽名雙方創建安全的協議和合約 。

標准 XML 簽名允許一個特殊的 Reference:不帶有 URI 屬性 Reference。 XML 簽名規范將其定義為接收方應用程序應當能夠識別在這種情況下應當使用的 對象。在 Lotus Forms 中,未包含 URI 的 Reference 表示整個 XFDL 文檔。因 此,需要一個定制 dereferencer 來知道如何從各種引用的 URI 中獲取所有被引 用數據(包括不帶 URI 的引用)。這種定制 dereferencer 實現了 javax.xml.crypto.URIDereferencer 並且被稱為 NoUriDereferencer。

NoUriDereferencer 使用委托(delegation)模式構建。在取消引用期間,如 果 URIReference 對象的 URI 屬性為 null,那麼表單文檔被作為八位字節流 (octet stream)返回(盡管返回 XML 文檔節點集會更加有效,要實現除相同文 檔引用以外的引用,javax.xml.crypto.URIDereference> 文檔必須使用八位 字節流)。對於所有其他 URI,類將委托由標准 JSR 105 實現提供的最初的默認 dereferencer。

清單 4. Dereferencer 代碼

public static class  NoUriDereferencer implements URIDereferencer
{
   private InputStream inputStream;

   public NoUriDereferencer(InputStream inputStream) throws  IOException
   {
     this.inputStream = inputStream;
   }

   public Data dereference(URIReference uriReference,  XMLCryptoContext context)
     throws URIReferenceException
   {
     if (uriReference.getURI() == null)
     {
       Data data = new OctetStreamData (this.inputStream);
       return data;
     }
     else
     {
       URIDereferencer defaultDereferencer =  XMLSignatureFactory.getInstance("DOM").
         getURIDereferencer();
       return defaultDereferencer.dereference(uriReference,  context);
     }
   }
}

驗證證書

對於 Lotus Forms 中的已簽署簽名,證書鏈被放入到 Signature 元素的 KeyInfo 中,其中第一個證書為已簽名證書。證書鏈可由 Java Certification Path API 進行檢驗。此外,如果維護了可信的接受方名稱列表,那麼可以比較已 簽名證書的接受方,看看它是否匹配可信列表中的接受方。這一步驟可以確保表 單的簽名者是可信的。

分步運行樣例代碼

所需的庫

Java 1.4 或 5 需要 JSR 105 實現。本文使用了 Apache XML Security Java 庫。由於 Java XML Digital Signature API 已經被包含到 JDK 6 中,因此 Java 6 不需要任何額外的 JAR 文件。

針對 Java 6 的說明

從本文的下載小節下載包含所有樣例代碼和表單的 ZIP 文件

將文件解壓縮到一個目錄中,比如 /forms

切換到目錄 /forms/xmldsig/bin: cd /forms/xmldsig/bin

確保使用了正確的 Java 版本:java -version

運行樣例代碼和表單:java xmldsig.FormVerification ../src/test/resource/form-signed.xfdl

一個已經發現的問題:在撰寫本文時,IBM JDK6 中的 XMLDSig 實現有一個名 稱空間標准化 bug,並且不支持 here() 函數,因此無法使用它對樣例表單進行 驗證。

針對 Java 1.4 或 5 的說明

從本文的下載小節下載包含所有樣例代碼和表單的 ZIP 文件

將文件解壓縮到一個目錄中,比如 /forms

從 http://xml.apache.org/security/dist/java-library/ 下載 Apache XML Security 庫 xml-security-bin-1_4_2.zip

將其解壓縮到 /forms(可以在隨後的表單中看到目錄 xml-security-1_4_2)

如果使用來自 Sun 的 Java 1.4 的話,將 xalan.jar、xml-apis.jar、 serializer.jar 從 /forms/xml-security-1_4_2/libs 復制到 jre/lib/endorsed(參見 http://santuario.apache.org/Java/installation.html)

切換到目錄 /forms/xmldsig/bin: cd /forms/xmldsig/bin

確保使用了正確的 Java 版本:java -version

運行樣例代碼和表單:java -classpath .;../../xml-security- 1_4_2/libs/xmlsec-1.4.2.jar;../../xml-security-1_4_2/libs/commons- logging.jar;../../xml-security-1_4_2/libs/xalan.jar xmldsig.FormVerification ../src/test/resource/form-signed.xfdl

結束語

Lotus Forms 使用針對 XForms 和 XML Signature 的 W3C 標准來表示它的數 字簽名。因此,可以根據開放標准輕松地對表單進行驗證,從而實現將其集成到 企業應用程序流程中。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved