SAML响应的XML签名怎么验证
技术百科
幻夢星雲
发布时间:2026-01-23
浏览: 次 SAML签名验证必须先对SignedInfo节点做排他性规范化(exclusive canonicalization),再用公钥验签;直接解Base64后验整个XML字节必然失败,因签名仅覆盖规范化后的SignedInfo子树。
Signature 验证不是解 Base64 后直接验字节 —— 你必须先对 SignedInfo 做排他性规范化(exclusive canonicalization),再用公钥验签。否则哪怕 XML 结构完全正确,sig.verify() 也一定失败。
为什么直接验 contentBytes 总是 false?
你当前的 verifySignature 方法传入的是整个响应 XML 的字节,但 SAML 签名只覆盖 SignedInfo 节点(含其子节点),且该节点在签名前已被按特定规则标准化(比如去掉无关空白、归一化命名空间声明)。直接传原始 XML 字符串或未规范化的节点内容,哈希值必然不匹配。
-
SignedInfo是签名的“输入原文”,不是整个Response - 规范化算法由
ds:CanonicalizationMethod@Algorithm指定,常见为http://www.w3.org/2001/10/xml-exc-c14n# - Java 原生
Node.getC14NMethod()不支持自动识别该算法,需手动调用org.jcp.xml.dsig.internal.dom.DOMCanonicalizer或用xmlsec库
用 xmlsec 库最稳(推荐)
Apache xmlsec 是 Java 生态验证 SAML 签名的事实标准,它自动处理:CanonicalizationMethod 解析、Reference URI 解析(如 #_response123456)、Transforms(如 enveloped-signature 剔

SignatureMethod 映射(rsa-sha256 / rsa-sha1 自动适配)。import org.apache.xml.security.signature.XMLSignature; import org.apache.xml.security.transforms.Transforms; import org.w3c.dom.Document; import org.w3c.dom.Element;// 已加载 Document doc Element sigElement = (Element) doc.getElementsByTagNameNS("https://www./link/573a77b45da4e86a0fc93e5f76cc99ce#", "Signature").item(0); XMLSignature signature = new XMLSignature(sigElement, ""); boolean isValid = signature.checkSignatureValue(publicKey); // 自动做 c14n + verify
- 别自己解析
ds:SignatureValue和ds:SignedInfo——xmlsec内部已封装全部逻辑 - 确保 classpath 有
xmlsec-3.0.3.jar(适配 Java 11+)和xmlsec-java-3.0.3.jar - 若用 Keycloak 发出的响应,注意其
SignatureMethod很可能是rsa-sha256,旧版xmlsec(如 2.x)默认不支持,必须升到 3.x
手撸验证时,三个关键点不能漏
如果因合规或轻量要求必须不用第三方库,务必确认以下三步都严格对齐 SAML 响应中的 ds:Signature 描述:
-
取对节点:用 XPath 定位到
ds:SignedInfo元素,不是ds:Signature根节点 -
做对规范化:调用
signedInfoNode.getC14NMethod().canonicalizeSubtree(signedInfoNode),且参数必须设为true(保留注释)或false(剔除注释),取决于CanonicalizationMethod@Algorithm是否带#WithComments -
验对算法:从
ds:SignatureMethod@Algorithm提取实际算法(如http://www.w3.org/2001/04/xmldsig-more#rsa-sha256),映射为 Java 支持的字符串(SHA256withRSA),而非硬写RSA-SHA1
SAML 签名验证真正难的不是代码,而是对 ds:Reference 中 URI、Transforms、DigestMethod 这三者的联动理解 —— 它们共同定义了“到底哪段字节被签了”,漏掉任意一个环节,就等于在验一份假数据。
# 的是
# 已被
# 很可能
# 必须先
# 设为
# 自动识别
# 不支持
# 再用
# internal
# http
# go
# java
# 字节
# xml
# 字符串
# 为什么
# node
# 封装
# 算法
# 命名空间
# dom
# apache
# 子树
# 公钥
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Python字符串操作教程_切片拼接与格式化详解
- PHP 中如何在函数内持久修改引用变量所指向的目标
- Python安全爬虫设计_IP代理池与验证码识别策
- PHP cURL GET请求:正确设置请求头与身份
- Python lxml的etree和Element
- php文件怎么变mp4保存_php输出视频流保存为
- Python项目维护经验_长期演进说明【指导】
- Win11怎么关闭系统透明度_Windows11个
- Win10怎么卸载鲁大师_Win10彻底卸载鲁大师
- Windows11怎么用“记事本”自动换行与编码
- 如何使用Golang进行HTTP服务性能测试_测量
- Python字符串处理进阶_切片方法解析【指导】
- 微信短链接怎么还原php_用浏览器开发者工具抓包获
- mac怎么退出id_MAC退出iCloud账号与A
- Win11怎么设置右键刷新选项_Windows11
- Windows怎样拦截WPS弹窗广告_Window
- PhpStorm怎么调试PHP代码_PhpStor
- Win11文件扩展名怎么显示_Win11查看文件后
- Win11怎么清理C盘系统日志_Win11清理系统
- php删除数据怎么清空表_truncate与del
- Win10如何卸载微软拼音输入法 Win10只保留
- Mac如何修改Hosts文件?(本地开发与屏蔽网站
- 如何使用Golang操作指针变量_Golang解引
- 如何优化Golang Web性能_Golang H
- Win11怎么关闭OneDrive同步_Win11
- windows如何修改文件默认打开方式_windo
- php删除数据怎么加限制_带where条件删除避免
- 如何使用正则表达式提取以编号开头、后跟多个注解的完
- Windows10如何删除恢复分区_Win10 D
- PHP怎么接收URL中的锚点参数_获取#后面参数值
- Win10怎样清理C盘爱奇艺缓存_Win10清理爱
- Windows10如何更改桌面背景_Win10个性
- 如何在Golang中实现WebSocket广播_使
- Win11怎么设置默认邮件客户端 Win11修改M
- php打包exe如何加密代码_防反编译保护方法【技
- Python日志系统设计与实现_高可观测性架构实战
- 如何使用Golang log记录不同级别日志_Go
- php嵌入式需要什么环境_搭建php+linux嵌
- Win11麦克风没声音怎么设置_Win11麦克风权
- Win11怎么清理C盘虚拟内存_Win11清理虚拟
- Win11怎么打开旧版计算器_Win11恢复传统计
- c++怎么实现高并发下的无锁队列_c++ std:
- C++如何将C风格字符串(char*)转换为std
- Win11怎么关闭系统推荐内容_Windows11
- PythonDocker高级项目部署教程_多容器管
- Mac的“调度中心”与“空间”怎么用_Mac多桌面
- Python脚本参数接收_sys与argparse
- Linux如何挂载新硬盘_Linux磁盘分区格式化
- C++中的Pimpl idiom是什么,有什么好处
- win11 OneDrive怎么彻底关闭 Win1

QQ客服