SAML: 为什么证书在签名中?

我必须实施 SSO 与 SAML 为我公司的网站(作为依赖方)。偏离航线的一个重要部分是签名的验证。下面是来自我们的合作伙伴公司(声明方)的示例 SAML 的签名部分:

<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
<ds:Reference URI="#_2152811999472b94a0e9644dbc932cc3" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:Transforms xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ec:InclusiveNamespaces PrefixList="ds saml samlp xs" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
<ds:DigestValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">bW1Os7+WykqRt5h0mdv9o3ZF0JI=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
cgrAN4T/UmobhrkkTi3miiRfbo0Z7aakSZjXuTWlZlu9jDptxPNbOFw8ZbYKZYyuW544wQqgqpnG
gr5GBWILSngURjf2N45/GDv7HMrv/NRMsRMrgVfFsKbcAovQdLAs24O0Q9CH5UdADai1QtDro3jx
nl4x7HaWIo9F8Gp/H1c=
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>MIIElzCCA3+gAwIBAgIQNT2i6HKJtCXFUFRB8qYsZjANBgkqhkiG9w0BAQUFADB3MQswCQYDVQQG
EwJGUjEOMAwGA1UEBxMFUGFyaXMxDDAKBgNVBAoTA3BzYTEgMB4GA1UECxMXY2VydGlmaWNhdGUg
YXV0aG9yaXRpZXMxKDAmBgNVBAMTH0FDIFBTQSBQZXVnZW90IENpdHJvZW4gUHJvZ3JhbXMwHhcN
MDkwODE5MDcxNTE4WhcNMTEwODE5MDcxNTE5WjCBhjELMAkGA1UEBhMCZnIxHzAdBgkqhkiG9w0B
CQEWEHBhc3NleHRAbXBzYS5jb20xGDAWBgoJkiaJk/IsZAEBEwhtZGVtb2IwMDEMMAoGA1UEChMD
cHNhMREwDwYDVQQLEwhwcm9ncmFtczEbMBkGA1UEAxMSVGVzdCAtIFBBU1NFWFQgREVWMIGfMA0G
CSqGSIb3DQEBAQUAA4GNADCBiQKBgQCuY1nrepgACvDSTLWk5A1cFOJSwDbl6CWfYp3cNYR0K3YV
e07MDZn+Rv4jo3SusHVFds+mzKX2f8AeZjkA3Me/0yiS9UpS9LQZu9mnhFlZRhmUlDDoIZxovLXN
aOv/YHmPeTQMQmJZu5TjqraUq7La1c187AoJuNfpxt227N1vOQIDAQABo4IBkTCCAY0wDgYDVR0P
AQH/BAQDAgWgMB8GA1UdIwQYMBaAFLceWtTfVeRuVCTDQWkmwO4U01X/MAwGA1UdEwEB/wQCMAAw
gbYGA1UdIASBrjCBqzCBqAYKKoF6ARfOEAEBBDCBmTBBBggrBgEFBQcCARY1aHR0cDovL3JldW5p
cy5pbmV0cHNhLmNvbS9hdXRvcml0ZS9QQy1BQy1Qcm9ncmFtcy5wZGYwVAYIKwYBBQUHAgIwSDAK
FgNwc2EwAwIBARo6UG9saXRpcXVlIGRlIENlcnRpZmljYXRpb24gQUMgUFNBIFBldWdlb3QgQ2l0
cm9lbiBQcm9ncmFtczBcBgNVHR8EVTBTMFGgT6BNhktodHRwOi8vaW5mb2NlcnQucHNhLXBldWdl
b3QtY2l0cm9lbi5jb20vQUMtUFNBLVBldWdlb3QtQ2l0cm9lbi1Qcm9ncmFtcy5jcmwwHQYDVR0l
BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBYGA1UdDgQPBA1BVVRPX0dFTkVSQVRFMA0GCSqGSIb3
DQEBBQUAA4IBAQCvRtP6bFkOUEHcqc6yUX0Q1Gk2WaAcx4ziUB0tw2GR9I0276JRJR0EGuJ/N6Fn
3FhLQrSPmS97Xvc9XmiI66fQUdg64g9YqBecdiQlUkR20VLgI6Nq8pldQlWjU2iYlkP15U7VF4Qr
0Pb2QiIljZUCKdv3qdED2Ri33za46LfykrlwZB0uhTVUxI/AEtjkKVFaZaqanJg+vJyZI5b30z7g
Ff8L3ht4Z7SFKdmY3IQSGzElIAAUfduzTJX0cwnGSU9D4BJu1BS8hWnYPwhk+nBJ7OFhXdwYQFWq
fhpBLq+ciJti9OMhcdCSIi0PbrOqzqtX7hZUQOvfShhCTJnl5TJJ</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>

我不明白的是,为什么证书在签名里?

我的意思是,通常我从公司得到一个证书,以一种安全的方式,所以我知道证书是从他们。当签名验证成功时,我知道我们的合作公司已经签字了。

但是当证书在 SAML-Response 的签名中时,任何人都可以发送它!我唯一知道的就是回复并没有被篡改。但重点是,我不知道是谁发送的 SAML。

有人能给我解释一下,这是怎么回事吗?

93227 次浏览

签名证书的公共部分在 SAML 消息中。这用于检查令牌本身的签名,当然也允许接收方告知谁发出了令牌并相应地对待它。

The fact that it's in there is part of the XML digital signature specs, it's not really anything SAML specific. Without the certificate how could you tell where the token came from, and how could you validate it?

XmlDSig 确实指定了其他方法,您可以通过主题、序列号、散列等标识签名密钥,但是这要假设接收方具有公共证书。对于 SAML,情况可能并非如此,因此嵌入了 X509证书的公共部分。

SAML 响应附带一个签名和该签名的公钥。

您可以使用公钥来验证 SAML 响应的内容是否与密钥匹配——换句话说——该响应肯定来自消息中拥有与公钥匹配的私钥的人,而且响应没有被篡改。

I don't know what tech you're working with, but in .Net you can check it like this:

// load a new XML document
var assertion = new XmlDocument { PreserveWhitespace = true };
assertion.LoadXml("The SAML XML that you were sent");


// use a namespace manager to avoid the worst of xpaths
var ns = new XmlNamespaceManager(assertion.NameTable);
ns.AddNamespace("samlp", @"urn:oasis:names:tc:SAML:2.0:protocol");
ns.AddNamespace("asrt", @"urn:oasis:names:tc:SAML:2.0:assertion");
ns.AddNamespace("dsig", @"http://www.w3.org/2000/09/xmldsig#");


// get nodes down to the signature
var responseNode = assertion.SelectSingleNode("/samlp:Response", ns);
var assertionNode = responseNode.SelectSingleNode("asrt:Assertion", ns);
var signNode = assertionNode.SelectSingleNode("dsig:Signature", ns);


// load the XML signature
var signedXml = new SignedXml(assertion.DocumentElement);
signedXml.LoadXml(signNode as XmlElement);


// get the certificate, basically:
//     signedXml.KeyInfo[0].Certificates[0]
// ...but with added casting
var certificate = GetFirstX509Certificate(signedXml);


// check the key and signature match
bool isSigned = signedXml.CheckSignature(certificate, true);

它只是检查信息是否来自它所说的人。您需要额外检查消息是否来自您信任的某个人,这种检查速度较慢——它需要包括撤销,并且可能需要验证整个证书链。

通常,这将是一个公钥列表,您可以从中接受 SAML 响应。

然后可以检查此消息是否未被篡改,是否来自您信任的人,因此可以对提供的 SAML 属性中提供的用户详细信息进行授权。

You 可以 already have the public key, meaning that the signature shouldn't need to include the public key again, but you could also have multiple possible known senders, or even a chain of known senders.

例如,您可能有两个受信任的提供程序——在任何一种情况下,您都要在检查是否信任任何一个提供程序之前检查消息是否被篡改。如果密钥不在签名中,那么断言可能会小一些,但是现在您必须事先知道断言来自哪个标识提供程序。

所以,实际上,公钥存在于签名中有两个主要原因:

  1. 篡改检查比身份检查快,而且如果公钥已知,可以隔离。
  2. 如果键在断言中,则支持多个标识要容易得多。

在 SAML 响应中指定公钥的原因是,标识提供程序的元数据可以指定多个公钥。这允许标识提供者(断言方)向服务提供者(依赖方)指定正确的公钥,以便用于验证 SAML 响应中的签名。

例如,断言方的元数据可能如下所示:

<KeyDescriptor>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>BQUAMCMBgN...XerfXHHEZYZs=</ds:X509Certificate>
</ds:X509Data>
<ds:X509Data>
<ds:X509Certificate>H24a88h7zl...2zo28hH5DK78=</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>

尽管 SAML 2.0没有强制要求包含公钥,但我还没有遇到过任何身份提供者在其 SAML 响应中不包含公钥的情况。如果没有在断言中指定公钥,那么它应该可以通过标识提供程序的元数据推断出来。

在信任响应中发送的公钥方面,公钥必须与标识提供程序的元数据中定义的公钥匹配。这些元数据细节通常是由你的客户提供的,他们希望使用 SSO 来访问你的应用程序——你会准确地知道需要查找什么样的公钥(也就是说,你可能会要求他们提供他们的身份提供者的元数据 URL,这样你就可以获取他们的元数据并下拉相关信息,如公钥、发行者端点等)。

如果随签名提供的公钥是元数据中未指定的公钥,那么 SAML 系统在验证签名时必须生成错误。

身份提供程序使用自己的私钥对相同的响应进行签名。在注册/SAML 元数据交换阶段,双方共享公钥证书。任何一方都可以拥有多个签名证书,并且可以免费使用其中的一个。对应的公钥已经与依赖方共享,因此在 SAML 响应中共享公钥只是通知依赖方(在数字验证时使用此证书)。