首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

使用 Java 驗證 Lotus Forms XML 數位簽名——基於 JSR 105 標準 API 實作

     編輯:Java 相關知識

Lotus Forms 採用 XFDL (Extensible Forms Description Language) 描述表單,XML 數位簽名封裝於文檔內部(Enveloped Signature),遵循 W3C XML Signature 標準。本文示範不依賴 Lotus Forms API,純用 JSR 105 (Java XML Digital Signature API) 驗證簽名表單,簡化跨廠商整合。

Lotus Forms 簽名結構解析

XForms 模型實例中內嵌 <ds:Signature>:

  • SignedInfo:包含規範化方法、簽名演算法、一或多個 Reference。
  • Reference (URI=""):無 URI 表示簽署整個 XFDL 文檔。Transform 鏈含 dsig2:XPath 過濾器,排除簽名元素自身(避免循環依賴)。
  • Reference (第二個):可選擇簽署簽名元數據(如 XAdES 時間戳)。
  • SignatureValue:Base64 編碼的簽名值,空值表示未簽名。
  • KeyInfo:含證書鏈,首張為簽名者證書。

圖示樣例簽名表單展示 UI 佈局與簽名欄位。

驗證核心流程

  1. 解析 XFDL 文檔,定位所有 xforms:instance 中 ds:Signature 且 SignatureValue 非空的節點。
  2. 對每個簽名,提取對應 XForms 實例子樹構建獨立 DOM 文檔(Instance Document),繼承所有祖先命名空間(遵循命名空間覆蓋規則)。
  3. 建立 DOMValidateContext,指定簽名節點與自定義 URIDereferencer。
  4. 使用 KeySelector 從 KeyInfo 提取公鑰驗證證書鏈(Java Certification Path API)。
  5. 解組 XMLSignature,調用 validate() 獲取布林結果。
  6. 彙總所有簽名結果,輸出表單整體驗證狀態。

關鍵技術細節

1. 命名空間繼承與複製

從 xforms:instance 克隆子樹建立新文檔時,必須將所有祖先元素的命名空間宣告複製到新文檔根元素,且最近定義優先(子節點覆蓋父節點同前綴)。代碼片段:

private void cloneNamespaces(Element src, Element target) {
    Map copied = new HashMap();
    Node node = src;
    while (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
        NamedNodeMap attrs = node.getAttributes();
        for (int i = 0; i < attrs.getLength(); i++) {
            Attr attr = (Attr) attrs.item(i);
            if (!XML_NS_URI.equals(attr.getNamespaceURI())) continue; // 僅處理 xmlns:*
            String qName = (attr.getPrefix() == null) ? attr.getLocalName() 
                            : attr.getPrefix() + ":" + attr.getLocalName();
            if (copied.containsKey(qName)) continue; // 最近優先
            Attr newAttr = target.getOwnerDocument().createAttributeNS(attr.getNamespaceURI(), qName);
            newAttr.setNodeValue(attr.getNodeValue());
            target.setAttributeNodeNS(newAttr);
            copied.put(qName, attr.getNodeValue());
        }
        node = node.getParentNode();
    }
}

2. 自定義 URIDereferencer 處理無 URI Reference

標準規定 Reference 無 URI 時由接收方應用決定引用對象。Lotus Forms 定義為整個 XFDL 文檔。實作 NoUriDereferencer 委派模式:

public static class NoUriDereferencer implements URIDereferencer {
    private InputStream formStream; // 原始 XFDL 輸入流
    public NoUriDereferencer(InputStream is) { this.formStream = is; }
    public Data dereference(URIReference uriRef, XMLCryptoContext ctx) {
        if (uriRef.getURI() == null) 
            return new OctetStreamData(formStream); // 返回原始文檔位元組流
        else
            return XMLSignatureFactory.getInstance("DOM").getURIDereferencer()
                   .dereference(uriRef, ctx); // 其他 URI 委派預設實作
    }
}

驗證時將此實例注入 DOMValidateContext。

3. 證書鏈驗證與信任錨點

從 KeyInfo 取得證書鏈,利用 CertPathValidator 驗證鏈路有效性。若維護受信任主體清單,再比對簽名者證書主體 DN,確保簽名者受信。

執行範例代碼

Java 6+:內建 JSR 105 實作,無額外 JAR。
Java 1.4/5:需 Apache XML Security (xmlsec.jar, commons-logging.jar, xalan.jar) 並配置 endorsed 目錄。

# Java 6
java xmldsig.FormVerification ../src/test/resource/form-signed.xfdl

# Java 1.4/5
java -cp .;../../xml-security-1_4_2/libs/xmlsec-1.4.2.jar;... xmldsig.FormVerification ...

已知問題:撰文時 IBM JDK 6 存在命名空間規範化 Bug 且不支援 here() 函數,無法驗證樣例表單,建議使用 Sun/Oracle JDK。

總結

Lotus Forms 簽名完全基於 W3C 標準,透過 JSR 105 標準 API 即可跨平台、跨廠商驗證,無廠商鎖定。核心在於:正確構建 Instance Document(命名空間繼承)、自定義 Dereferencer 處理無 URI Reference、標準證書鏈驗證。隨附完整源碼與樣例表單供下載實驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved