Lotus Forms 採用 XFDL (Extensible Forms Description Language) 描述表單,XML 數位簽名封裝於文檔內部(Enveloped Signature),遵循 W3C XML Signature 標準。本文示範不依賴 Lotus Forms API,純用 JSR 105 (Java XML Digital Signature API) 驗證簽名表單,簡化跨廠商整合。
XForms 模型實例中內嵌 <ds:Signature>:
dsig2:XPath 過濾器,排除簽名元素自身(避免循環依賴)。圖示樣例簽名表單展示 UI 佈局與簽名欄位。
xforms:instance 中 ds:Signature 且 SignatureValue 非空的節點。DOMValidateContext,指定簽名節點與自定義 URIDereferencer。KeySelector 從 KeyInfo 提取公鑰驗證證書鏈(Java Certification Path API)。XMLSignature,調用 validate() 獲取布林結果。從 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();
}
}
標準規定 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。
從 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、標準證書鏈驗證。隨附完整源碼與樣例表單供下載實驗。