深入了解 SecureConversation – 第二部分
深入探讨增强型 Web 服务的安全协议,第二部分:分析响应消息。
引言
在本文的第一部分中,我们分析了 Cardspace 与我自己的安全令牌服务(STS),也称为身份提供程序,实现之间的 RST/RSTR 交换的请求消息。在第二部分中,我们将深入探讨 STS 响应我们之前分析的请求消息而发送回来的响应消息。
与第一部分一样,我们只关注保护 RST/RSTR 数据的安全协议。此协议用于在新版 Web 服务规范中传输多种类型的交换。我以后会写另一篇文章来介绍用于将 SAML 令牌从 STS 传输到请求方的 RST/RSTR。
背景
本文不涉及任何代码,但是假设您对 XML 有深入的了解,并且对基本的加密学有所理解,例如使用对称和非对称算法进行加密和签名。我附带了一个小程序,可以帮助您理解本文中使用的加密学基础。
如果您还没有阅读本文的第一部分,我强烈建议您阅读,因为我们将使用第一部分中详细介绍的概念。
第二部分:包含 RSTR(RequestSecurityTokenResponse)的响应 SOAP 消息
SOAP 消息的结构
您可以通过第一部分的相同章节来获取 SOAP 消息结构的描述。与请求消息一样,分析整个消息所需的许多有用信息都包含在标头中的 Security
元素里。我们将首先介绍如何解密 Security
元素中的加密内容。
解密标头中的加密元素
与请求消息一样,我们必须获取整个消息中所有加密数据的列表。这通过 Security
中的 ReferenceList
元素给出。此元素如下所示。
<e:ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:DataReference URI="#_3" />
<e:DataReference URI="#_6" />
</e:ReferenceList>
在此消息中,我们有两个加密元素。让我们先关注 ID 为 "_6" 的元素,它位于 Security
元素中。正如我们在第一部分中看到的,EncryptedData
包含一个 KeyInfo
,它有助于查找用于加密数据的密钥。整个 EncryptedData
元素在列表 1 中进行了描述。
列表 1
<e:EncryptedData
Type="http://www.w3.org/2001/04/xmlenc#Element" Id="_6"
xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference>
<o:Reference URI="#_4" />
</o:SecurityTokenReference>
</KeyInfo>
<CipherData xmlns="http://www.w3.org/2001/04/xmlenc#">
<CipherValue>L9uCbEDhVjcB4gTBqC3GtyaiGGY8P7...
oDJ7pB21bAA48xoLzk2ks</CipherValue>
</CipherData>
</e:EncryptedData>
如果我们查看 EncryptionMethod
元素,我们会发现数据已使用 128 位密钥的对称 AES 算法加密。因此,我们需要做的第一件事是找到用于加密的密钥,以便解密数据。此信息由 KeyInfo
元素提供。此元素引用了一个 ID 为 "_4" 的元素。在 Security
元素中查找,我们可以发现它指向以下 DerivedKeyToken
元素。
<sc:DerivedKeyToken u:Id="_4"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-
200401-wss-wssecurity-utility-1.0.xsd"
xmlns:sc="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:KeyIdentifier
ValueType="http://docs.oasis-open.org/wss/oasis-
wss-soap-message-security-1.1#EncryptedKeySHA1">
UbPUL5eLKdw25Xptln4DOGN3YLI=
</o:KeyIdentifier>
</o:SecurityTokenReference>
<sc:Offset>0</sc:Offset>
<sc:Length>16</sc:Length>
<sc:Nonce>i2uYqtgSQCE/KE7C4E86UQ==</sc:Nonce>
</sc:DerivedKeyToken>
这种元素类型在本文的第一部分中已经见过。DerivedKeyToken
元素包含计算派生密钥的信息。用于计算此密钥的算法是 PSHA1,它需要一个主密钥、一个随机数和一个标签。标签值由 Microsoft 固定为字符串值“WS-SecureConversationWS-SecureConversation”。SecurityTokenReference
用于获取主密钥值。在请求消息中,主密钥被引用为 EncryptedKey
。此主密钥实际上由两个消息共享。响应消息将由请求方处理,请求方是创建请求消息的方,因此知道它生成的该主密钥。
SecurityTokenReference
的目的是让请求方能够检查 STS 使用的密钥是否是请求方自己生成的密钥。
SecurityTokenReference
包含一个 KeyIdentifier
,其 ValueType
属性指示我们正在查找的密钥的引用是 EncryptedKeySHA1
。这意味着 Base64 值 UbPUL5eLKdw25Xptln4DOGN3YLI= 代表了在请求消息中发送的加密密钥值的 SHA1。如果您参考第一部分,该值为 XsApZde4j3w35HGt…YVHkbY0MFZIg=。请求方必须保留此加密数据并将其映射到主密钥值。与使用 RSA 加密一样,对于相同的数据,密文总是不同的,因此请求方无法再次加密主密钥来获得加密值。当然,要进行计算,您必须首先获取 Base 64 数据的二进制值。
您可以在第一部分的文章中使用的工具来检查这一点。
有了主密钥,现在就可以计算派生密钥并使用它来解密加密元素中的数据。应用解密后,您应该会得到列表 2 中的数据。这些数据是 ASCII 值,实际上是消息的 Signature
元素。
列表 2
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1" />
<Reference URI="#_0">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>cf8q2Vw2rW5blihu0xWaxCdU4QA=</DigestValue>
</Reference>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>LDb2+mKJ9h0YUEQ2IILUtKjSWGg=</DigestValue>
</Reference>
<Reference URI="#uuid-8346502e-ebb7-b631-0704-403dd21ca6ec-1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>fJ9Jk5L5Du0pY6FSIQnuBRKtVmI=</DigestValue>
</Reference>
<Reference URI="#_5">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>SjeSg51ZvqDFfu46wsHK2+QmZEo=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>7ddH6wlhQNJPw6VRMORxP6eD37Q=</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-
200401-wss-wssecurity-secext-1.0.xsd">
<o:Reference URI="#_4" />
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
解密正文中的加密数据
既然我们已经解密了 Signature
元素,解密正文将易如反掌!Body
的 EncryptedData
元素如下所示:
<e:EncryptedData
Type="http://www.w3.org/2001/04/xmlenc#Content"
Id="_3" xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-
200401-wss-wssecurity-secext-1.0.xsd">
<o:Reference URI="#_4" />
</o:SecurityTokenReference>
</KeyInfo>
<CipherData xmlns="http://www.w3.org/2001/04/xmlenc#">
<CipherValue>83suvxZAJ+4hOeo/baEFfdu20yKkU4unm1QJ4N9
WrUVOa...BVNOKR5yumvi/aQ==</CipherValue>
</CipherData>
</e:EncryptedData>
如果我们查看 KeyInfo
,可以看到它引用了同一个密钥,这意味着我们只需要使用我们计算出的派生密钥来解密 Signature
。对数据运行 AES128 解密算法将为我们提供一些 ASCII 数据,即 RequestSecurityTokenResponse
XML 元素。该元素由 STS 响应请求消息正文中解密的 RST 而构建。
接下来需要做的是验证请求方收到的消息的完整性。之前,我们解密了一个 Signature
元素。该元素包含用于验证它的信息。
验证签名
如果您参考请求消息中的 Signature
元素,并在本文第一部分中对其进行了分析,您会发现它的结构非常相似。由于没有进一步的兴趣来详细介绍 Signature
的验证过程,您可以在本文第一部分中找到它。
关注点
在这两部分中,我们已经看到了大量的概念和信息。WCF 完全隐藏了 WS-* 协议,使安全功能的使用就像配置文件中的一行 XML 一样简单。然而,您可以看到幕后发生了许多事情。当您激活安全功能,或者需要处理互操作性(就像我需要做的那样)时,理解实际发生的情况尤为重要。
因此,我希望本文能引起您学习 WCF 中使用的安全协议的兴趣。当然,这只是一个特定互操作性案例的研究,但我认为它可以帮助您更好地理解如何在 WCF 中使用安全性,就像它通过撰写本文帮助我更清楚地认识到这些功能一样。