You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Python验证ADFS返回的SAML登录响应签名

SAML HTTP-Redirect绑定签名验证步骤及Python实现

验证前置说明

ADFS通过HTTP-Redirect绑定返回SAML响应时,签名是HTTP层级的签名,而非SAML XML内部的签名,这是绝大多数验证失败的核心原因:不要对解密后的XML内容做签名校验,要对URL参数拼接串做校验。

具体验证步骤

  • 步骤1:获取请求中的三个核心参数的原始值(未经过URL解码的原始查询参数值):SAMLResponse、SigAlg,如果请求携带RelayState也需要同步获取
  • 步骤2:按照SAML 2.0规范构造待验签字符串:
    1. 将所有参与签名的参数按参数名字符升序排列,顺序为RelayState(如有)→SAMLResponse→SigAlg
    2. 每个参数按照参数名=URL编码后的参数值格式拼接,参数值编码遵循RFC 3986规则,*不做编码
    3. 多个参数之间用&拼接,将最终拼接结果转为UTF-8编码的字节流
  • 步骤3:从ADFS元数据中获取签名用的X.509公钥证书,导出为PEM格式
  • 步骤4:将请求中的Signature参数值做Base64解码,得到原始签名字节流
  • 步骤5:使用公钥、SigAlg指定的签名算法,验证待验签字节流和签名字节流是否匹配

常见错误说明

  • 错误使用SAML POST绑定的验证逻辑,对解密/解码后的SAML XML内容做验签,Redirect绑定的签名不涉及XML内容
  • 拼接待验签串时顺序错误,或者对参数值做了URL解码后再拼接
  • 混淆加密证书和签名证书:签名验签用的是ADFS的公钥,不是你用来解密SAML响应的业务侧私钥

Python实现方案

依赖安装

执行以下命令安装加密库:

pip install cryptography

示例代码

from urllib.parse import quote
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend
import base64

# -------------------------- 配置项开始 --------------------------
# 填入请求中获取的原始参数值(不要做URL解码)
SAML_RESPONSE_RAW = "替换为你的SAMLResponse参数原始值"
SIG_ALG_RAW = "替换为你的SigAlg参数原始值"
SIGNATURE_RAW = "替换为你的Signature参数原始值"
# 可选:如果有RelayState参数,取消下方注释并填入原始值
# RELAY_STATE_RAW = "替换为你的RelayState参数原始值"

# 填入ADFS签名证书PEM内容,从ADFS元数据中获取
ADFS_SIGN_CERT_PEM = b"""
-----BEGIN CERTIFICATE-----
替换为你的ADFS签名公钥证书内容
-----END CERTIFICATE-----
"""
# -------------------------- 配置项结束 --------------------------

# 1. 构造待验签字节流
param_list = []
# 有RelayState则先加入,按参数名字母升序排列
# param_list.append(f"RelayState={quote(RELAY_STATE_RAW, safe='*')}")
param_list.append(f"SAMLResponse={quote(SAML_RESPONSE_RAW, safe='*')}")
param_list.append(f"SigAlg={quote(SIG_ALG_RAW, safe='*')}")
sign_data = "&".join(param_list).encode("utf-8")

# 2. 加载ADFS公钥
cert = load_pem_x509_certificate(ADFS_SIGN_CERT_PEM, backend=default_backend())
public_key = cert.public_key()

# 3. 解码签名值
signature_bytes = base64.b64decode(SIGNATURE_RAW)

# 4. 执行验签
try:
    public_key.verify(
        signature_bytes,
        sign_data,
        padding.PKCS1v15(),
        # 如果SigAlg为其他算法,此处对应修改,如sha1则用hashes.SHA1()
        hashes.SHA256()
    )
    print("签名验证通过")
except Exception as e:
    print(f"签名验证失败:{str(e)}")

内容的提问来源于stack exchange,提问作者Kadhiresh Gajendiran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 08:24:04