原文:http://idior.cnblogs.com/articles/381534.html

使用用户名和密码来验证用户的身份是最普通也最常见的方法,虽然在安全性方面也比较弱,由于其运用的广泛性还是成为了WS-Security目前所支持的Security Token之一。其原理非常简单,用户在发送请求的时候,在Soap head中加入自己的用户名以及密码,接受请求的Service通过之前与Client建立的共享密码来验证密码的合法性从而实现鉴别用户的功能。
不过实际运用起来就不能考虑的那么简单了,该方法主要存在两个问题: 1. 在SOAP包中传输密码怎么保证密码的安全性? 2. 怎么从用户名密码中获得签名和加密所需要的密钥?

针对第一个问题有三种解决方案: 1. 使用运输层的安全协议(如SSL)来保证明文密码的安全性。 2. 对明文密码做摘要后再传送给Service。 3. 利用从密码派生出来的密钥来代替直接使用密码来实现身份鉴别。

第一种方法采用了WS-Security结合运输层的安全协议(SSL)来保证密码的安全,在本系列一开始的文章已经描述过运输层安全协议的缺点,所以在此不对该方法做详细介绍。

第二种方法类似于HTTP Digest Authentication,先来看一段使用该方法的示例:
<wsse:UsernameToken>     <wsse:Username>NNK</wsse:Username>     <wsse:Password Type="...#PasswordDigest">          weYI3nXd8LjMNVksCKFV8t3rgHh3Rw==     </wsse:Password>     <wsse:Nonce>WScqanjCEAC4mQoBE07sAQ==</wsse:Nonce>     <wsu:Created>2003-07-16T01:24:32Z</wsu:Created> </wsse:UsernameToken> 从中看出这里使用了PasswordDigest类型的Password,从Password的内容也可以看出这里没有使用明文密码的形式。另外还多出了wsse:Nonce和wsu:Created两个元素。 其中Password的内容的计算公式如下: Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
读者可能比较奇怪wsse:Nonce和wsu:Created这两个元素的作用。为什么不直接SHA-1(password) ? 这样做是为了避免重放(Replay)攻击。假设Alice以摘要的形式向Service发送了密码,如果Bob此时截获了Alice发送的密码摘要,然后再用它向Service发送请求,那么Service将误认为Bob也是合法用户。当我们加入Nonce和Created元素之后,Service可以检查收到的消息中的Nonce是否已经收到过了,或者在一段时间(5min)内是否收到了相同用户名密码,从而避免重放攻击的危险。不过使用PasswordDigest方式要求Service必须拥有密码的明文形式,也就是说Service可以看到每个用户的密码,这对用户来说增加了风险。因为通常情况下用户的密码是以hash的形式保存在Service端的,从而保证用户的信息不被泄漏。 尽管通过PasswordDigest可以避免密码的明文传播,而且通过引入wsse:Nonce和wsu:Created可以避免重放攻击的危险,但是如果Bob能够把传送中的密码摘要完全的拦截下来(使它无法传送到Service),然后利用拦截下来的密码去冒充Alice去请求Service,那么Service将束手无策。为此,我们引入了第三种方法。

第三种方法和Kerberos协议中KDC向Client传送TGT的方式类似。 我们可以看出前两种方式用户都将自己的密码发送给Service用于身份鉴别,难道为了证明自己的身份就必须把密钥(这里是密码)直接告诉别人吗?其实问题的关键在于Client能向Service证明它拥有只有C与S知道的密钥。而证明拥有的最直接方法就是告诉对方这个密钥,然后由Service比较这个密钥是否和它所知道的密钥一致,从而鉴别用户的身份。但是这种方法如前所述存在多种缺陷。既然仅仅需要Client证明它知道这个密钥,那么Client可以用这个密钥对一段消息做一个签名,然后将消息和签名同时发送给Service,Service用它所知道的Client的密钥也对同样的消息做一次签名,通过比较两个签名是否一致就可以确认Client是否真的拥有它的密钥。同样通过加密的方法Client也可以向Service证明自己是否真的拥有密钥(因为只有C与S密钥一致Service才能解密出用C密钥加密的消息)。这样一旦Client在消息中加入自己的一些特有信息(比如IP),即便Bob截获了消息但是由于他并不知道真正的密钥,看不到那些特有信息,也就无法冒充Alice。

通过这种间接证明拥有密钥的方法,我们同时解决了文章一开始提出的第二个问题: 怎么从用户名密码中获得签名和加密所需要的密钥?
只要对密码做一些处理就可以从中派生出密钥。当然为了安全起见我们希望每次派生出来的密钥都不一样,这样就可以避免多次使用同一密钥而导致密钥被破解。下面就是WS-Security对密钥派生的元素定义:
<wsse:UsernameToken wsse:Id=”…”>     <wsse:Username>…</wsse:Username>     <wsse11:Salt>…</wsse11:Salt>     <wsse11:Iteration>…</wsse11:Iteration> </wsse:UsernameToken> 其中Salt是导致密钥变化的因子,Iteration是密钥派生时Hash的次数。 密码的派生公式如下: K1 = SHA1( password + Salt)  K2 = SHA1( K1)    … Kn = SHA1 ( Kn-1)

可以看到此时在UsernameToken已经不再包含Password元素,因为Client将通过使用从Password派生出密钥做签名做加密的方式来证明它拥有密钥,从而证明自己的身份。

由此看出第三种办法相对来说安全性大大提高了,但是在实际应用中以上介绍的三种的方法都不被推荐使用。 第三种方法仍旧存在以下两个缺陷: 1. 直接使用密码派生密钥,同以往临时产生的会话密钥相比,密码一旦破解,所有由改密码派生的密钥也被破解。由于密码长期不变, 那么随后所有使用该密码加密的消息都没有安全性可言。而且该密码可能还被用于Client与其他Service的交互,那么被破解后带来的损失就大多了。 2. 用户密码必须以明文形式保存在Service端。

因此,在微软的WSE对安全的默认支持方式中采用了UsernameToken和Service端Certification的组合的方式来表示Security Token。下图就是WSE中已经实现的UsernameForCertificate对SOAP Envelop的扩展结构。
             

从中可以看到SOAP Head中的wsse:UsernameToken已经被加密,被xenc:EncryptedData所替代,查看其Token Reference发现加密使用的Key来自xenc:EncryptedKey。如果你完整阅读了本系列的文章,你将不会对它太陌生,在XML Encryption中曾经对它的来由做了详细介绍。

Note由于使用对称密钥加密效率高,所以通常会使用对称密钥来加密数据,但是如何让消息的接受也获得对称密钥则成了一个问题。消息发送方不可能将对称密钥也随消息传递给消息接收方,此时利用非对称密钥来实现加密所用的对称密钥的传递成为了一个比较好的选择。EncrptedKey就是实现此种功能的扩展元素。

Client随机产生了一个对称密钥并用它来加密和签名SOAP Envelop中的其他元素(如UsernameToken),然后通过使用Service(消息接受方)的公钥(由于是公钥可以方便获取)来加密该对称密钥,以保证只有Service能够获得Client随机产生的对称密钥,从而达到验证消息完整性,解密数据以及鉴别用户身份的目的。以下是采用这种方式保证安全的SOAP Envelop的示例:
<?xml version="1.0" encoding="utf-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope"     xmlns:ds="http://www.w3.org/2000/09/xmldsig#"     xmlns:xenc="http://www.w3.org/2001/04/xmlenc"     xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">     <SOAP-ENV:Header>         <wsse:Security             xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/secext">             <wsse:UsernameToken>                 <wsse:Username>HotelService</wsse:Username>                 <wsse:Password>myword</wsse:Password>                                       </wsse:UsernameToken>             <xenc:EncryptedKey wsu:id="userSysmetricKey">                 <xenc:EncryptionMethod                     Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>                  <ds:KeyInfo>                        <wsse:SecurityTokenReference>                            <wsse:KeyIdentifier                               ValueType="...oasis-wss-soap-message-security-1.1#ThumbPrintSHA1">                                     LKiQ/CmFrJDJqCLFcjlhIsmZ/+0=                            </wsse:KeyIdentifier>                         </wsse:SecurityTokenReference>                     </ds:KeyInfo>                 <xenc:CipherData>                     <xenc:CipherValue>G2wDCq24FsgBGerE...</xenc:CipherValue>                 </xenc:CipherData>                 <xenc:ReferenceList>                     <xenc:DataReference URI="#DiscountResponse"/>                 </xenc:ReferenceList>             </xenc:EncryptedKey>             <ds:Signature>                 <ds:SignedInfo>                     <ds:CanonicalizationMethod                         Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>                     <ds:SignatureMethod                         Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"/>                     <ds:Reference URI="#DiscountedBookingForPartnersResponse">                         <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"/>                         <ds:DigestValue>JwFsd3eQc0iXlJm5PkLh7...</ds:DigestValue>                     </ds:Reference>                 </ds:SignedInfo>                 <ds:SignatureValue>BSxlJbSiFdm5Plhk...</ds:SignatureValue>                     <ds:KeyInfo>                                             <wsse:SecurityTokenReference>                                <wsse:Reference URI="#userSysmetricKey"                             ValueType="...oasis-wss-soap-message-security-1.1#EncryptedKey"/>                           </wsse:SecurityTokenReference>                     </ds:KeyInfo>             </ds:Signature>         </wsse:Security>     </SOAP-ENV:Header>     <SOAP-ENV:Body wsu:Id="DiscountedBookingForPartnersResponse">         <s:GetSpecialDiscountedBookingForPartnersResponse                 xmlns:s="http://www.MyHotel.com/partnerservice ">             <xenc:EncryptedData                 wsu:Id="DiscountResponse"                 type="http://www.w3.org/2001/04/xmlenc#Element">                 <xenc:EncryptionMethod                     Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc "/>                 <CipherData>                 <CipherValue>XD6sFa0DrWsHdehrHdhcW0x...</CipherValue>                 </CipherData>             </xenc:EncryptedData>         </s:GetSpecialDiscountedBookingForPartnersResponse>     </SOAP-ENV:Body> </SOAP-ENV:Envelope>

Note以上介绍的方法UsernameForCertificate仅被WS-Security1.1支持。因为在1.1中才支持使用对称密钥签名。

如此,之前所提到的问题都可以解决了,让我们回顾一下: Q: 在SOAP包中传输密码怎么保证密码的安全性? A: 使用密钥加密,只有接受方能解密密码。

Q: 怎么从用户名密码中获得签名和加密所需要的密钥? A:  随机产生密钥,并通过接受方的公钥加密,保证密钥不被别人所知。

Q: 如何避免重放攻击? A: 由于其他人无法获得密钥,所以即便截获消息也无法冒充。

Q: 直接使用密码派生密钥被破解了怎么办? A: 密钥不再从密码派生,而是每次随机产生,即便被破解也不会影响其他的消息和其他的服务。

Q: 用户密码必须以明文形式保存在Service端? A: 由于密码被加密而不是做摘要所以不需要Service拥有明文密码。
应用场景: B2C网上购物,每个用户都有各自的用户名密码,而且可以方便的获得Server端的Certification。(比如Amazon)
参考资料: OASIS Kerberos Token Profile 1.1 Protect Your Web Services Through The Extensible Policy Framework In WSE 3.0

UsernameToken 【转】的更多相关文章

  1. Configuring WS-Security UsernameToken and WS-SecureConversation (Symmetric Connection Creation)

    Context This procedure provides a detailed process of all necessary steps to secure Web Services wit ...

  2. Error in WCF client consuming Axis 2 web service with WS-Security UsernameToken PasswordDigest authentication scheme

    13down votefavorite 6 I have a WCF client connecting to a Java based Axis2 web service (outside my c ...

  3. Atitit java onvif 开源类库 getProfiles getStreamUri

    Atitit java onvif 开源类库 getProfiles getStreamUri 1. ONVIF Java Library by Milgo1 1.1. https://github. ...

  4. 扩展Wcf call security service, 手动添加 Soap Security Head.

    有次我们有个项目需要Call 一个 Java 的 web service, Soap包中需要一个 Security Head <soapenv:Header> <wsse:Secur ...

  5. BizTalk发布WS-Security的web services

    最近做个项目,biztalk跟OTM(Oracle Transportation Management)系统做对接,双方通过web services通讯,这部分是BizTalk发布WS-Securit ...

  6. BizTalk调用WS-Security的web services

    最近做个项目,biztalk跟OTM(Oracle Transportation Management)系统做对接,双方通过web services通讯,这部分是BizTalk调用OTM的web se ...

  7. webservice调用服务端数据时给soapenv:Envelope 添加自定义的命名空间

    最近做第三方接口,服务端需要 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/&qu ...

  8. CXF 入门:创建一个基于WS-Security标准的安全验证(CXF回调函数使用,)

    http://jyao.iteye.com/blog/1346547 注意:以下客户端调用代码中获取服务端ws实例,都是通过CXF 入门: 远程接口调用方式实现 直入正题! 以下是服务端配置 ==== ...

  9. 关于RESTFUL API 安全认证方式的一些总结

    常用认证方式 在之前的文章REST API 安全设计指南与使用 AngularJS & NodeJS 实现基于 token 的认证应用两篇文章中,[译]web权限验证方法说明中也详细介绍,一般 ...

随机推荐

  1. VS2010/MFC编程入门之三十(常用控件:树形控件Tree Control 上)

    前面两节为大家讲了列表视图控件List Control,这一节开始介绍一种特殊的列表--树形控件Tree Control. 树形控件简介 树形控件在Windows系统中是很常见的,例如资源管理器左侧的 ...

  2. Polya

    using namespace std; typedef long long LL; const int MAXN = 1e3 +10; const LL MOD = (LL)1 << 6 ...

  3. java提升路线

    转载自:http://blog.csdn.net/a151296/article/details/43658853 作为一名即将从事java开发的应届毕业生,迷茫中,转载此篇文章,作为提升自己的学习方 ...

  4. javascript数组总结

    数组是一个有序的集合,javascript数组中的元素的类型可以是任意的,同一个数组不同元素之间的类型也是可以不同的.数组也是对象,有个length属性,记录数组的长度. 创建数组有两种方法: 数组直 ...

  5. springcloud15---zuul-fallback

    package com.itmuch.cloud; import org.springframework.boot.SpringApplication; import org.springframew ...

  6. HTTP从入门到入土(3)——TCP三次握手

    TCP三次握手 客户端与服务器之间互相发送HTTP请求响应之前需要先进行TCP连接,因为HTTP是一个无连接.无状态协议,不存在连接的概念,只有请求和响应的概念.而请求和响应实际上只是数据包,他们需要 ...

  7. 【eclipse】运行maven项目clean tomcat7:run报错

    问题: Failed to execute goal org.apache.maven.plugins:maven-clean-plugin:2.5:clean 解决: 关闭进程javaw.exe,然 ...

  8. Spring思维导图,让spring不再难懂(一)

    写在前面 很多人在微信公众号中给我留言说想看spring的思维导图,正好也打算写.与其他框架相比,spring项目拥有更多的模块,我们常用的ioc,mvc,aop等,这些是spring的主要板块.一篇 ...

  9. perl模块终极解决方案--转载

    不管别人怎么说,反正我是非常喜欢perl语言的! 也会继续学习,以前写过不少perl模块的博客,发现有点乱,正好最近看到了关于local::lib这个模块. 居然是用来解决没有root权限的用户安装, ...

  10. python stat获取文件信息

    import os statinfo = os.stat('qqq.txt') print(statinfo,"\n") print(statinfo.st_mode) 输出 os ...