Apache Santuario 配置参数详解
Apache Santuario 3.0.6 配置参数详解:secure validation 安全校验开关、每 Reference 最多 5 个 Transform 限制、摘要与签名算法支持清单(MD5 构造期拒绝实测)、资源解析器注册、Init 初始化与内置 config.xml、XMLCipher 加密入口,含生产配置模板与 FAQ,全程实测。
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/(groupIdorg.example,artifactIdxmlsec-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.xml | Init.isInitialized() 查询状态 |
| XMLCipher 算法 | 单个加解密对象 | XML Encryption 算法 URI | XMLCipher.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,无需额外配置文件。
八、参考链接
- Apache Santuario 官网:http://santuario.apache.org/
- JSR-105(XML Digital Signature API)规范:https://www.jcp.org/en/jsr/detail?id=105
- W3C XML Signature 语法与处理规范:https://www.w3.org/TR/xmldsig-core/
- W3C XML Encryption 语法与处理规范:https://www.w3.org/TR/xmlenc-core/