享受代码,享受人生

SOA is an integration solution. SOA is message oriented first.
The Key character of SOA is loosely coupled. SOA is enriched
by creating composite apps.
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Identity Federation Part 2

Posted on 2006-05-15 09:32  idior  阅读(3848)  评论(3编辑  收藏  举报

在Part 1中我们已经将SAML Assertion运用到了WS-Security中, 并完全理解了它的原理. 但是有一个重要的问题却被我们所遗漏, SAML Assertion从何而来? 之前,我们一直在讨论SAML的原理,语法以及如何使用它保障Web Service的安全. 但是并没有涉及如何获得SAML Assertion. 再来回顾一下使用SAML的示意图:

           

回忆之前的介绍, 发现主要集中在图中的阶段3和阶段4, 而对阶段1,2并没有太多涉及.
而阶段1,2正是用户在本域中验证自己的身份从而获得SAML Assertion的过程. 在SAML的规范中同时定义了SAML的XML语法和SAML协议的XML语法,其中的SAML规范中的协议则规定了如何获得SAML Assertion的方法,不过在这里我并打算介绍利用SAML协议的方法,而是采用另一套Security Token的获取协议WS-Trust, 因为它属于WS-*协议族,被微软和IBM众多公司所支持,并且在WSE中已经有了协议的具体实现。

下面将介绍如何利用WS-Trust协议获得SAML Token,并利用该Token向Service发出请求,整个过程还将利用WS-Security协议抵御来自各方面的安全威胁。从Client要求获得SAML Token到Service返回结果,主要经历了如下4个阶段:
1. Client向STS(Security Token Service)请求SAML Token。
2. STS处理RST(Request Security Token)后向Client返回结果。
3. Client向Service发出请求。
4. Service处理请求并返回结果。

           
下面对每个阶段做详细介绍:

1. Client向STS(Security Token Service)请求SAML Token。

1.1 Client创建一个UsernameToken。
1.2 Clinet创建一个RST(Request Security Token)。
1.3 Client 利用签名保证RST的完整性。
1.4 Client加密RST中的敏感数据。
1.5 Client向STS发送RST。

1.1 Client创建一个UsernameToken。
Client根据自己的用户名密码创建出UsernameToken. 用于STS对用户身份的鉴别.

1.2 Clinet创建一个RST(Request Security Token)。
Client根据要请求的服务以及服务所需要的Token类型(这里是SAML)创建出RST, 示例如下:

<wst:RequestSecurityToken xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
   <wst:TokenType>
      .../oasis-wss-saml-token-profile-1.1#SAMLV1.1
   </wst:TokenType>
   <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue
   </wst:RequestType>
   <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
      <wsa:EndpointReference>
         <wsa:Address>
            http://www.RetireAccounts.com/partnerservice/
         </wsa:Address>
      </wsa:EndpointReference>
   </wsp:AppliesTo>
   <wst:Lifetime>
      <wsu:Expires>2005-12-08T21:54:46Z</wsu:Expires>
   </wst:Lifetime>
</wst:RequestSecurityToken>

其中TokenType指定所要求的Token类型,如X.509 Token, Kerberos Token,这里是SAML Token . RequestType指定请求的类型, 如请求STS发布一个Security Token 或请求STS验证某个Security Token. AppliesTo指定请求服务的地址.

1.3 Client 利用签名保证RST的完整性。
Client随机产生一个对称密钥,并利用STS的公钥加密该对称密钥并传递给STS,这样就可以这里Client和STS安全的拥有了同一个对称密钥,此时Client可以使用对称密钥技术来签名RST, 而STS将来也可以用获得对称密钥来验证签名,从而保证RST的完整性. 此处采用了WSE自带的usernameForCertificateSecurity方式, 在之前的文章也曾经有过介绍, 如下图所示:


                 

1.4 Client加密RST中的敏感数据。
这里的敏感数据指得是UsernameToken中的Password. 利用在签名阶段产生的密钥,可以轻易的实现加密的功能, 在上图中已经体现了这点.

1.5 Client向STS发送RST。
Client将经过签名加密的RST 发送给Service.

 

2. STS处理RST(Request Security Token)后向Client返回结果。

2.1 STS解密RST中的加密数据.
2.2 STS验证RST的完整性.
2.3 STS验证RST中的身份凭据(此处是UsenameToken)
2.4 STS创建与用户身份对应的SAML Token.
2.5 STS将包含SAML Token的RSTR发送给Client.

2.1 STS解密RST中的加密数据.
STS利用自己的公钥解密出EncryptedKey中包含的对称密钥, 然后利用该对称密钥解密出UsernameToken以及其他被Client加密的数据.

2.2 STS验证RST的完整性.
采用第一步中得到的对称密钥验证消息中的签名。

2.3 STS验证RST中的身份凭据(此处是UsenameToken)
STS将解密获得的UsenameToken与之前保存在AD,LDAP或者数据库的数据比较,从而验证用户的身份。

2.4 STS创建与用户身份对应的SAML Token.
根据用户的身份以及属性,STS将为之创建相应的SAML Token。前面我们已经介绍过SAML的语法以及SAML的主体确认方法,其中使用了Hold-of-Key的确认方法,并且在当时的介绍中采用的是Client的私钥。但是并不是每个用户都有数字证书的,在这种情况下用户如何证明自己是SAML Token的拥有者呢?
而且在之前我们只考虑了Client向Service发送SAML Token以实现身份验证,但是之间的消息的完整性和机密性都没有考虑。其实要保证消息的完整性和机密性就是需要它们之间有一个共享的密钥。如果Client采用Service的公钥加密一个由自己产生的对称密钥,再利用它签名加密消息也不失为一个办法,但问题在于Service根本就不信任Client,你现在所做的正是要让它们建立信任关系。
因此,我们需要一种解决方案,可以使得Client与Service之间安全的共享一个对称密钥,而且能让Client利用这个密钥证明自己是SAML Token的拥有者。
我们知道Service并不信任Client,但是它信任STS,所以由STS产生密钥,然后将它安全的分发给Client和Service,此时Client与Service就可以利用该密钥签名加密消息,而且Client可以通过该密钥证明自己就是在SAML Token中宣称主体(Hold-of-key),这样Client就不再需要为此申请一个数字证书了(要求每个客户都有自己的数字证书是不现实的)。
因此创建SAML Token的过程如下:

STS首先生成该用户的认证信息和相关属性,然后生成一个对称密钥(Client和Service都可以通过传递Entropy的方式影响密钥的生成结果),为了让该密钥只被Service接收到,我们使用Service的公钥对其加密,再加入到SAML Token的SubjectConfirmation元素中。最后由STS使用自己的私钥对整个以上内容加以签名,从而保证该Token确实是由STS签发的,并保证它不会被篡改。最后的结果如下图所示:

                  

通过SAML Token我们可以将密钥安全的传递给Service,那么如何将密钥安全的传递给Client,从而让Client和Service安全的通信,并使得Client可以以此密钥证明自己是SAML Token中描述的主体。将密钥传递给Client将由RSTR来完成。

2.5 STS将包含SAML Token的RSTR发送给Client.
RSTR中主要包含了之前创建的SAML Token,以及为了证明Client身份所需要的那个对称密钥。将STS之前产生的对称密钥以明文的形式包含到RSTR的RequestedProofToken元素中,就可以实现密钥向Client的传递。RSTR的结构大致如下图所示:


                  

为了不让SAML Token和RequestedProofToken元素中密钥被别人窃取,在此将使用Client向STS发送RST时在SOAP Head中包含的密钥对整个RSTR加密并签名,然后将结果返回给Client。
 
             

          
Note仔细观察你会发现采用此种方式的WS-Trust在原理和Kerberos协议非常相像。


3. Client向Service发出请求。

3.1 Client创建请求消息。
3.2 Client对消息签名。
3.3 Client对消息加密。
3.4 Client将请求消息发送给Service。

3.1 Client创建请求消息。
Client首先根据请求的服务创建出相应的SOAP Envelop。然后将从STS中获得的RSTR中包含的SAML Token原封不动的添加到SOAP Envelop的Head中。
3.2  Client对消息签名。
Client从RSTR的RequestedProofToken元素中获得STS为Client和Service创建的对称密钥。 为了避免每次使用同一密钥签名(容易受到离线攻击),Client将利用PSHA1算法从中派生出一个临时密钥,并使用它对请求消息签名。
3.3 Client对消息加密。
和签名的方法类似,利用PSHA1算法从获得的对称密钥中派生出一个临时密钥,并使用它对请求消息中的敏感信息加密。
3.4 Client将请求消息发送给Service。
在经过以上对请求消息的处理后,Client将请求消息发送给Service。

4. Service处理请求并返回结果。
 
4.1 Service解密请求消息。
4.2 Service验证请求消息的签名。
4.3 Service验证请求消息中的SAML Token。
4.4 Service处理请求并返回结果。(可选)

4.1 Service解密请求消息。
Service从Client的请求消息的Head中取出用于该用户身份鉴别的SAML Token, 再利用自己的私钥将SAML Token中STS为Client和Service创建的对称密钥解密出来。Service在获得对称密钥后根据Client派生密钥时所用的参数,将此时会话的派生密钥也计算出来,利用它Service就可以将请求消息的加密部分解密。

4.2 Service验证请求消息的签名。
和解密过程类似,Service在计算出签名所用的派生密钥后,利用它验证签名,从而判断消息是否被篡改。

4.3 Service验证请求消息中的SAML Token。
Service从Client的请求消息的Head中取出SAML Token后,首先判断该Token是否在有效期内,其次验证该Token的签名,从而判断该Token是否由自己所信任的STS签发。最后根据Token中的主体验证方法判断Token所断言的主体是否就是消息的发送方,  由于此处采用的是Hold-of-Key的方法,只要之前我们能解密并验证请求消息的签名,就说明Client确实是Hold-of-Key(那个STS为它们创建的对称密钥),这样我们就可以完全的信任在SAML Token对Client所做出的断言(l例如Client在STS的验证信息和Client的一些相关属性)。

4.4 Service处理请求并返回结果。(可选)
如果请求消息有返回结果,那么Service将利用它与Client共享的对称密钥派生出一个新的密钥用于签名加密发送给Client的返回结果。

通过以上四个阶段的介绍,我们了解了如何使用SAML来实现用户信息的移动,以及使用WS-Trust来获取SAML,并且利用STS在本没有直接信任关系的Client和Service之间建立共享密钥的详细过程。在整个过程中,我们还使用了WS-Security来保证其中交互信息的安全。

以上一整套的解决方案,似乎已经可以很好的解决文章在一开始提出的问题---Identity Federation。通过SAML提供的Identity Portable的功能,RetireAccounts.com公司可以安全的向伙伴公司Pharma456.com的员工John提供它的服务,John也可以从RetireAccounts.com公司那获得伙伴公司员工特有的优惠待遇。从理论上看,似乎已经没有什么太多的问题,但是现实情况往往没那么理想。当我们从现实问题来考虑的时候,会发现上述的方案存在两个缺陷,其中还有一个缺陷是致命的。
问题主要出在Service端,如同我们不太可能为每个用户创建一个电子证书类似,为公司内的每一个服务创建一个电子证书也是不太现实的,因为一个公司可能存在多个服务,而每个服务又部署不在不同的位置。当然服务的数量相对于用户的数量要小的多,这个问题还是能够接受的。而第二个问题就直接破坏了上述方案实施的可能性。在这里为了能够使用SAML来传递用户信息,你不得不要求每个服务都能够处理SAML Token,而要想实现这点,在现实环境下就成了几乎不可能完成的任务。我们知道Web Service是用于集成的,其中很重要的一点就是集成现有的解决方案。如果在RetireAccounts.com公司现有的服务所采取的安全措施都是基于Kerberos或是X.509的方式的话,你如何能强制要求它们都实现对SAML的支持,修改现有系统的安全基础设施的成本实在太大了。
由此看出仅仅依靠WS-Trust已不能解决以上的问题,WS-Federation应运而生。

              

注意在过程1和过程7中所使用的Token根据公司所采取的安全基础设施,每个公司都可能有所不同。但是这并不会影响我们实现Identity Federation, 最重要的是我们在过程4中使用SAML来实现用户信息的移动,而过程1和过程7所采用的Token的不同都可以通过定制不同的STS来解决,而并不会影响到已有的解决方案,面向对象中的“Open to extend, Close to modify”在这里同样适用。

Web Services Security系列文章