Apache Santuario 配置参数详解

Apache Santuario 3.0.6 配置参数详解:secure validation 安全校验开关、每 Reference 最多 5 个 Transform 限制、摘要与签名算法支持清单(MD5 构造期拒绝实测)、资源解析器注册、Init 初始化与内置 config.xml、XMLCipher 加密入口,含生产配置模板与 FAQ,全程实测。

Apache SantuarioApache Santuario2026/10/12

Apache Santuario 配置参数详解

目标:梳理 Apache Santuario 3.0.6(制品名 xmlsec)在签名与验签链路上的全部配置入口与关键参数:JSR-105 上下文属性(secure validation)、安全限制(Transform 数量上限、算法白名单)、原生 API 的资源解析器注册与 Init 初始化配置文件、XMLCipher 加密入口。所有结论均基于 jar 字节码与运行时实测。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS
  • Java:OpenJDK 17.0.20.1
  • 构建工具:Apache Maven 3.6.3
  • Santuario 版本:xmlsec 3.0.6(3.x 线最新稳定版)
  • 示例工程目录:/root/xmlsec-demo/(groupId org.example,artifactId xmlsec-demo)
  • 演示账号:root

版本回显:

$ java -version
openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1-22.04-Ubuntu, mixed mode, sharing)
$ mvn -version
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "6.8.0-138-generic", arch: "amd64", family: "unix"

二、前置条件

  • 已按《Apache Santuario 安装指南》完成 JDK 17 与 Maven 安装,并创建 Maven 工程。

  • 工程根 pom 中引入唯一依赖(传递依赖由 Maven 自动解析为 slf4j-api 1.7.36、jakarta.xml.bind-api 3.0.1、commons-codec 1.18.0、woodstox-core 6.5.1):

    <dependency>
        <groupId>org.apache.santuario</groupId>
        <artifactId>xmlsec</artifactId>
        <version>3.0.6</version>
    </dependency>
    
  • 下文用 $J 指代本地仓库中的制品 jar,先赋值:

    J=~/.m2/repository/org/apache/santuario/xmlsec/3.0.6/xmlsec-3.0.6.jar
    

三、参数查看与修改方式

Santuario 的配置分散在四个入口,按作用域从大到小:

3.1 JVM 层:标准 Java 参数

通过 MAVEN_OPTS(Maven 场景)或 java -X 参数(直跑场景)控制堆内存等。示例进程实测:

export MAVEN_OPTS="-Xms256m -Xmx512m"

生效验证(jps -v,进程号以实际为准):

$ jps -v | grep -F 'Xmx512m'
6855 ValidateExample -Xms256m -Xmx512m

3.2 JSR-105 上下文属性:XMLCryptoContext.setProperty

DOMSignContext / DOMValidateContext 均实现 javax.xml.crypto.XMLCryptoContext,可用 setProperty 设置按次生效的属性。Santuario 在该上下文上读取的核心布尔属性为 org.apache.jcp.xml.dsig.secureValidation(名称来源见 5.1 实测)。典型用法:

vc.setProperty("org.apache.jcp.xml.dsig.secureValidation", Boolean.TRUE);

3.3 原生 API 全局配置:Init 与内置配置文件

原生层(org.apache.xml.security.*)通过 Init.init() 完成一次性全局初始化,读取 jar 内置的 org/apache/xml/security/resource/config.xml(附 config.dtd)。查看内置配置资源:

$ unzip -l $J | grep 'resource/config'
    org/apache/xml/security/resource/config.dtd
    org/apache/xml/security/resource/config.xml

3.4 原生 API 解析器注册:ResourceResolver 静态方法

外部引用如何取回(本地文件、HTTP、XPointer 等)由 ResourceResolver 的静态注册方法控制,详见 5.5。

四、参数总览

配置项 / 参数作用域取值设置或查看方式
JVM 堆内存进程建议 -Xms256m -Xmx512m 起步MAVEN_OPTS 或 java -X;jps -v 验证
org.apache.jcp.xml.dsig.secureValidation单次签名/验签上下文Boolean.TRUE / Boolean.FALSE,未显式设置时不启用DOMSignContext / DOMValidateContext.setProperty
每 Reference Transform 数上限secure validation 开启时固定 5(DOMReference.MAXIMUM_TRANSFORM_COUNT)无需设置,超限直接报错
DigestMethod 白名单secure validation 开启时SHA1/SHA224/SHA256/SHA384/SHA512、RIPEMD160、SHA3 系列;MD5 被拒绝XMLSignatureFactory.newDigestMethod(...)
SignatureMethod 白名单secure validation 开启时RSA、ECDSA、DSA、HMAC、RSA-PSS(RSAandMGF1)、ED25519/ED448 等家族XMLSignatureFactory.newSignatureMethod(...)
资源解析器原生 API 全局内置 5 个 Resolver 实现ResourceResolver.register* 静态方法
库初始化原生 API 全局Init.init() 读取内置 config.xmlInit.isInitialized() 查询状态
XMLCipher 算法单个加解密对象XML Encryption 算法 URIXMLCipher.getInstance(...) 静态工厂

五、核心参数详解

5.1 secure validation(安全校验开关)

属性名 org.apache.jcp.xml.dsig.secureValidation 来自 jar 内 Utils.class 字符串实测:

$ unzip -p $J org/apache/jcp/xml/dsig/internal/dom/Utils.class | strings | grep -iE 'secure'
secureValidation
(org.apache.jcp.xml.dsig.secureValidation

Utils.secureValidation(XMLCryptoContext) 经 getProperty 读取该布尔属性,因此对 DOMSignContext 与 DOMValidateContext 都可用 setProperty 置 TRUE 生效(本批签名与验签示例均已开启并实测通过):

DOMValidateContext vc = new DOMValidateContext(publicKey, sigs.item(0));
vc.setProperty("org.apache.jcp.xml.dsig.secureValidation", Boolean.TRUE);

开启后的行为变化由两项机制构成:摘要/签名算法白名单收紧(见 5.3、5.4),以及每 Reference 的 Transform 数量硬上限(见 5.2)。验签示例的实测输出:

$ mvn -q compile exec:java -Dexec.mainClass=org.example.ValidateExample
signature valid: true
ref[0] validity status: true

5.2 每 Reference 最大 Transform 数(5 个)

DOMReference.class 字符串实测:

$ unzip -p $J org/apache/jcp/xml/dsig/internal/dom/DOMReference.class | strings | grep -iE 'secure|md5|transform'
MAXIMUM_TRANSFORM_COUNT
JA maximum of 5 transforms per Reference are allowed with secure validation
*http://www.w3.org/2001/04/xmldsig-more#md5
" when secure validation is enabled

(行首的 J、*、" 为 strings 提取常量池时的伪影,实际消息为 "A maximum of 5 transforms per Reference are allowed with secure validation"。)

即:secure validation 开启时,每个 Reference 最多允许 5 个 Transform,超过则在处理该 Reference 时报错。设计上这可防止攻击者用超长 Transform 链实施拒绝服务。

5.3 摘要算法(DigestMethod)支持清单与 MD5 禁用

DOMDigestMethod.class 字符串实测支持以下摘要算法 URI:

http://www.w3.org/2000/09/xmldsig#sha1
http://www.w3.org/2001/04/xmldsig-more#sha224
http://www.w3.org/2001/04/xmlenc#sha256
http://www.w3.org/2001/04/xmldsig-more#sha384
http://www.w3.org/2001/04/xmlenc#sha512

另有 RIPEMD160 与 SHA3_224/256/384/512 实现类常量。

MD5 在构造期即被拒绝。以下测试类尝试用 MD5 URI 构造 DigestMethod——创建 /root/xmlsec-demo/src/main/java/org/example/Md5DigestTest.java:

package org.example;

import org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI;

import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.Collections;

public class Md5DigestTest {
    public static void main(String[] args) throws Exception {
        try {
            // 1) 构造使用 MD5 摘要算法的 Reference
            XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", new XMLDSigRI());
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(2048);
            KeyPair kp = kpg.generateKeyPair();

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            org.w3c.dom.Document doc = dbf.newDocumentBuilder().parse(new File("invoice.xml"));

            DigestMethod md5 = fac.newDigestMethod("http://www.w3.org/2001/04/xmldsig-more#md5", null);
            Transform tf = fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null);
            Reference ref = fac.newReference("", md5, Collections.singletonList(tf), null, null);
            SignedInfo si = fac.newSignedInfo(
                    fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null),
                    fac.newSignatureMethod(SignatureMethod.RSA_SHA256, null),
                    Collections.singletonList(ref));
            KeyInfoFactory kif = fac.getKeyInfoFactory();
            KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kif.newKeyValue(kp.getPublic())));

            // 2) 签名(MD5 摘要)
            fac.newXMLSignature(si, ki).sign(new DOMSignContext(kp.getPrivate(), doc.getDocumentElement()));
            System.out.println("[MD5] signed with MD5 digest digestValue computed");

            // 3) 关闭 secure validation 验签
            DOMValidateContext vcOff = new DOMValidateContext(kp.getPublic(),
                    doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature").item(0));
            System.out.println("[MD5] validate (secureValidation OFF): "
                    + fac.unmarshalXMLSignature(vcOff).validate(vcOff));

            // 4) 开启 secure validation 验签
            DOMValidateContext vcOn = new DOMValidateContext(kp.getPublic(),
                    doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature").item(0));
            vcOn.setProperty("org.apache.jcp.xml.dsig.secureValidation", Boolean.TRUE);
            System.out.println("[MD5] validate (secureValidation ON): "
                    + fac.unmarshalXMLSignature(vcOn).validate(vcOn));
        } catch (Exception e) {
            System.out.println("[MD5] exception: " + e.getClass().getName() + ": " + e.getMessage());
        }
    }
}

实测输出:

$ mvn -q compile exec:java -Dexec.mainClass=org.example.Md5DigestTest
[MD5] exception: java.security.NoSuchAlgorithmException: unsupported algorithm

fac.newDigestMethod("http://www.w3.org/2001/04/xmldsig-more#md5", null) 在构造期即抛出 NoSuchAlgorithmException: unsupported algorithm,签名流程根本无法开始。生产上应统一使用 SHA256 及以上摘要算法。

5.4 签名算法(SignatureMethod)家族

DOMSignatureMethod.class 常量实测覆盖以下家族:

  • HMAC 系列(HMAC-SHA1/SHA224/SHA256/SHA384/SHA512 等);
  • RSA 系列(SHA1/SHA224/SHA256/SHA384/SHA512 withRSA);
  • RSAandMGF1(RSA-PSS);
  • ECDSA 系列(含 SHA3 变体);
  • DSA(SHA1withDSA / SHA256withDSA);
  • EDDSA(ED25519 / ED448);
  • RIPEMD160withRSA 等。

示例签名使用 RSA-SHA256:

fac.newSignatureMethod(SignatureMethod.RSA_SHA256, null)

对应签名产物中的 URI(signed.xml 实测):http://www.w3.org/2001/04/xmldsig-more#rsa-sha256。

5.5 资源解析器(ResourceResolver)注册

原生 API 通过静态方法注册解析器,javap 实测签名:

$ javap -classpath $J org.apache.xml.security.utils.resolver.ResourceResolver | grep -E 'register|getInstance'
  public static void register(java.lang.String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
  public static void registerAtStart(java.lang.String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
  public static void register(org.apache.xml.security.utils.resolver.ResourceResolverSpi, boolean);
  public static void registerClassNames(java.util.List<java.lang.String>) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
  public static void registerDefaultResolvers();

jar 内置 5 个解析器实现(jar tf 实测):

org/apache/xml/security/utils/resolver/implementations/ResolverAnonymous.class
org/apache/xml/security/utils/resolver/implementations/ResolverDirectHTTP.class
org/apache/xml/security/utils/resolver/implementations/ResolverFragment.class
org/apache/xml/security/utils/resolver/implementations/ResolverLocalFilesystem.class
org/apache/xml/security/utils/resolver/implementations/ResolverXPointer.class

配置要点:registerAtStart 将解析器插到查找链最前,优先级最高;registerDefaultResolvers 恢复内置默认集合。安全起见,若业务只需处理同文档内引用(URI=""、#id),可只保留 Fragment/XPointer 相关解析器,移除 DirectHTTP 等外链解析能力。

5.6 原生 API 初始化与 config.xml

原生层的初始化入口 org.apache.xml.security.Init(javap 实测):

$ javap -classpath $J org.apache.xml.security.Init
Compiled from "Init.java"
public class org.apache.xml.security.Init {
  public static final java.lang.String CONF_NS;
  public org.apache.xml.security.Init();
  public static final synchronized boolean isInitialized();
  public static synchronized void init();
  static {};
}

初始化前后状态可用以下诊断类观察——创建 /root/xmlsec-demo/src/main/java/org/example/DiagExample.java:

package org.example;

import org.apache.xml.security.Init;

import javax.xml.crypto.dsig.XMLSignatureFactory;

public class DiagExample {
    public static void main(String[] args) throws Exception {
        // A) 未显式指定提供者时,getInstance("DOM") 解析到哪个实现
        XMLSignatureFactory def = XMLSignatureFactory.getInstance("DOM");
        System.out.println("[A] default getInstance(\"DOM\") provider: " + def.getProvider().getName()
                + " | class: " + def.getProvider().getClass().getName());

        // B) 原生 API 的初始化状态(org.apache.xml.security.Init)
        System.out.println("[B] Init.isInitialized() before: " + Init.isInitialized());
        Init.init();
        System.out.println("[B] Init.isInitialized() after : " + Init.isInitialized());
    }
}

实测输出:

$ mvn -q compile exec:java -Dexec.mainClass=org.example.DiagExample
[A] default getInstance("DOM") provider: XMLDSig | class: org.jcp.xml.dsig.internal.dom.XMLDSigRI
[B] Init.isInitialized() before: false
[B] Init.isInitialized() after : true

Init.init() 读取 jar 内置的 org/apache/xml/security/resource/config.xml(config.dtd 同在),Init.class 字符串实测含命名空间常量 http://www.xmlsecurity.org/NS/#configuration 与错误提示 Error in reading configuration file - Configuration element not found。该文件随 jar 分发,一般无需改动;Init.init() 幂等,全局调用一次即可。

5.7 XMLCipher 与 XML Encryption

XML 加密入口 org.apache.xml.security.encryption.XMLCipher 通过静态工厂创建(javap 实测全部 5 个重载):

$ javap -classpath $J org.apache.xml.security.encryption.XMLCipher | grep -E 'getInstance|encryptData|decryptToByteArray' | head -5
  public static org.apache.xml.security.encryption.XMLCipher getInstance(java.lang.String) throws org.apache.xml.security.encryption.XMLEncryptionException;
  public static org.apache.xml.security.encryption.XMLCipher getInstance(org.apache.xml.security.encryption.Serializer, java.lang.String) throws org.apache.xml.security.encryption.XMLEncryptionException;
  public static org.apache.xml.security.encryption.XMLCipher getInstance(java.lang.String, java.lang.String) throws org.apache.xml.security.encryption.XMLEncryptionException;
  public static org.apache.xml.security.encryption.XMLCipher getInstance(java.lang.String, java.lang.String, java.lang.String) throws org.apache.xml.security.encryption.XMLEncryptionException;
  public static org.apache.xml.security.encryption.XMLCipher getInstance() throws org.apache.xml.security.encryption.XMLEncryptionException;

算法以 XML Encryption 标准算法 URI 字符串传入首个参数;其余重载用于自定义序列化器、JCA provider 等场景。创建加密对象后经 encryptData / decryptToByteArray 完成加解密(方法签名见上方 javap 输出)。

六、生产配置模板

推荐的验签基线配置:显式 Santuario 提供者 + secure validation 常开 + SHA256 系算法 + 堆内存下限保障。

// 1) 工厂:显式使用 Santuario 提供者,避免解析到 JDK 内置实现
XMLSignatureFactory fac = XMLSignatureFactory.getInstance(
        "DOM", new org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI());

// 2) 验签上下文:定位 Signature 并开启 secure validation
DOMValidateContext vc = new DOMValidateContext(publicKey,
        doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature").item(0));
vc.setProperty("org.apache.jcp.xml.dsig.secureValidation", Boolean.TRUE);

// 3) unmarshal + 核心验证
XMLSignature signature = fac.unmarshalXMLSignature(vc);
boolean ok = signature.validate(vc);

配套约定:

  • 算法基线:摘要 SHA256(http://www.w3.org/2001/04/xmlenc#sha256)及以上;签名 RSA-SHA256(http://www.w3.org/2001/04/xmldsig-more#rsa-sha256)及以上;禁用 MD5(构造期即被拒绝)。
  • 堆内存:export MAVEN_OPTS="-Xms256m -Xmx512m"(或 java 直跑附加 -Xms256m -Xmx512m),jps -v 确认生效。
  • Transform 链:默认 enveloped + 可选 canonicalization 即可满足绝大多数场景,勿超 5 个。

七、常见问题

Q1:为什么 newDigestMethod 传 MD5 的 URI 会抛异常? Santuario 3.0.6 在工厂构造阶段即校验算法,MD5 属于被拒绝的弱摘要算法,抛 NoSuchAlgorithmException: unsupported algorithm(实测)。改用 DigestMethod.SHA256 即可。

Q2:secure validation 设置后没生效? 该属性挂在具体的 DOMSignContext / DOMValidateContext 上,按次生效:每次创建上下文后都要重新 setProperty,不能设置一次全局复用。属性名必须精确为 org.apache.jcp.xml.dsig.secureValidation。

Q3:每个 Reference 超过 5 个 Transform 会怎样? secure validation 开启时直接报错,消息为 "A maximum of 5 transforms per Reference are allowed with secure validation"(DOMReference 内置限制,MAXIMUM_TRANSFORM_COUNT)。应精简 Transform 链,而不是关闭 secure validation。

Q4:Init.isInitialized() 返回 false 有影响吗? 原生 API(org.apache.xml.security.*)使用前需要完成初始化。调用一次 Init.init() 即置为 true(实测 false → true),读取的是 jar 内置 config.xml,无需额外配置文件。

八、参考链接