基于xmpp的协议即时通讯软件开发--理论(一)

转载请注明:http://blog.csdn.net/u013022222/article/details/49834483


我在大二下学期的时候接触安卓,当时学完基础之后一直想做一个软件。想做的东西有很多,但唯独对即时通讯这块非常感兴趣,我很好奇微信背后实现的原理是什么。一番周折,我接触到了xmpp协议。如今闲暇,遂提笔记录当初开发学习之路。前几部分比较枯燥,主要讲解xmpp协议内容,后面会进入实战部分


xmpp起源非常之久(我那时候估计还在上幼稚园): 基本语法语义最初是由 Jabber 开源社区在 1999 年开 发的。2002 年,XMPP 工作组授权开发一个 Jabber 协议的改写本,将适用于 IETF 的即时消息(IM)与出席技术。


如今的xmpp大多是以客户-服务器架构设计的,客户端通过一个xmpp基于tcp与服务器进行连接,而服务器之间也可以通过tcp进行通信。其中服务器呢,有一下几个职能:

1) 管理连接其它实体的会话,以 XML 流格式(第 4 节)在已授权的客户端、服务器以及其它实体间来回传送。
2) 通过 XML 流在实体间路由具有合适地址的 XML 节。大多数与 XMPP 兼容的服务器设想有能力存储客户端的数据(例:基于 XMPP即时消息与出席应用的用户的联系列表);在这种情况下,XML 数据由服务器自身代表客户端直接处理,并不路由到其它实体。


xmpp的寻址:

处于历史原因,这个被称作jid.

jid= [ node "@" ] domain [ "/" resource ]
domain = fqdn / address-literal
fqdn = (sub-domain 1*("." sub-domain))
sub-domain= (internationalized domain label)
address-literal = IPv4address / IPv6address

所以标识一个即时消息用户、用户连接的服务器、用户连接的资源(例如:特别的客户端)的形式就为:

<[url=mailto:user@host/resource]user@host /resource[/url]>

但这个是最简单的例子,如果我们想实现更复杂的功能比如聊天室:一个提供多用户聊天服务的特别聊天室,可以以< [url=mailto:room@service]room@service[/url]>(“room”是聊天室名,“service”是多用 户聊天服务的主机名)作为地址。并且,此聊天室的特别拥有者可能以< [url=mailto:room@service/nick]room@service/nick[/url]>(“nick”是此拥有者的房间 昵称)作地址,许多其它 JID 类型均有可能(例如:<domain/resource>可能是一个服务器端脚本或服务)。


使 presence-­‐aware 实体间能够相互迅速的、异步交换相关的小负载的结构化信息有两种
基本元素:XML 流与 XML 节。


XML 流定义:

XML 流是一个容器,用于网络上任意两实体间交换 XML 元素。XML 流的开始是以一个起始的 XML<stream>标记(有合        适的属性与命名空间声明)表示,XML 流的结尾以一个结束的 XML</stream>标记表示。在流的生命周期中,初始化它的实体能够通过流发送极多的 XML 元素,元素与 XML 节(定义在此,<message/>,       <presence/>,        或        <iq/>元素由缺省命名空间验证)都用于协商流(例:协商使用 TLS或使用 SASL。“初始流”是从初始实体(通常是一个客户端或服务器)到接收实体(通常是一个服务器)的协商,并被看作与从初始实体到接收实体的会话一致。初始流能从初始实体到接收实体单向通信;为了能够从接收实体到初始实体的信息交换,接收实体必须在反方向协商一个流(“响应流”)。      


XML 节定义:

XML 节是一个不连续的结构化信息语义单元,通过 XML 流从一个实体发送到另一个实体。XML 节以根</stream>的直接子层存在,如果它匹配产品内容[XML],则可以很好的平衡。任何 XML 节的开始都由深度为 1 的 XML 流(例如:<presence>)的开始标记元素来清楚的表示, XML 节的结尾由相应的深度为 1 的关闭标记来清楚的表示。为传送想要的信息,一个 XML 节可能包含必要的子元素(带有属性,元素,XML 字符数据)。在此定义的仅有的XML 节是<message/>,<presence/>,<iq/>元素,由流的缺省命名空间验证,在 XML 节 中描述;为传输层安全(TLS:Transport  Layer  Security)协商,SASL 协商,或服务器回叫而发送的 XML 元素,并不会当作 XML 节来考虑。


考虑一个客户端与服务器的会话例子。为了连接到服务器,客户端必须初始化一个 XML流:发送一个起始的<stream>标记给服务,可选先于一个指定 XML 版本的文本声明与字符编码支持(参考文本声明的内容;也可参考字符编码)。服从本地策略与所提供的服务,服务器接下来应该回复另一个 XML 流给客户端,再次可选先于一个文本声明。一但客户端完成了 SASL 协商,客户端可以通过流发送极多的 XML 节给网络上的任意容器。当客户端想关闭流时,它简单发送一个关闭</stream>标记给服务器(也可以由服务器来关闭流),从这以后,客户端与服务器都应终止潜在的连接(通常是一个 TCP 连接)。        
       
习惯于将 XML 考虑成以文档为中心的人可能希望看到客户端与服务器的会话作为两个末端开口的(自由回答的)XML 文档的组成部分:一个从客户端到服务器, 另一个从服务器到客户端。从这个观点看,根<stream/>元素可被认为是每个“文档”的文档实体,两个“文档”都由通过两个 XML 流发送的 XML 节的集聚来建立。然而,这种观点仅是一种方便; XMPP
并不以文档处理,而是以 XML 流或 XML 节来处理。  本质上,那么,一个 XML 流充当了所有通过会话发送的 XML 节的信封。可用图简


绑定到 TCP       
      虽然将一个 XML 流结合到一个[TCP]连接上不是必须的(例如:两个实体能通过其它诸如[HTTP]投票选举机制而彼此互连),此说明也只定义了        XMPP 到 TCP 的绑定。在客户端到服务器端通信的上下文中,服务器必须允许客户端为了从客户端到服务器与服务器到客户端的 XML 节发送共享的一个单TCP 连接。在服务器到服务器的通信上下文中,服务器必须使用一条 TCP 连接用于从服务器到其对等服务器的 XML 节传送,另一条 TCP 连接(由对等初始化)用于对其等服务器到服务器的 XML 节传送,总共有两条 TCP 连接。      

流安全       
     当在 XMPP1.0 中协商 XML 流时,TLS 应当按 TLS 应用所定义的来使用,SASL必须按 SASL所定义的来使用。“初始流” (例如:从初始实体到接收实体的流)与“响应流”(例如:从接收实体到初始实体的流)必须被分别保护,即使双向安全可能已通过相互的认证机制所建立。实体 不应当在流被认证之前,尝试通过流发送 XML 节,但如果这样做了,那么,其它实体不准接受此类节,并应当返回一个<non-­‐authorized/>流错误,然后终止两端的 XML 流与潜在的 TCP 连接;注意,这只适用于 XML 节(例如: <message/>,       <presence/>,       <iq/>元素,由缺省命名空间检查)并不适用于流协商(例如:用于协商使用 TLS或使用 SASL)的 XML 元素



流属性       
              流元素属性如下:       
                  1)        to—‘       to’属性应当仅用于从初始实体到接收实体的 XML 流头中,并且必须被设成一个接收实体服务的主机名。 ‘to’属性不应当设在接收实体回应初始实的XML          流头中;然而,如果‘to’属性包括在内,它应当被初始实体默默忽略。       
                  2)        from—‘       from’属性应当仅用于从接收实体到初始实体的 XML 流头中,并且必须被设成一个接收实体服务的主机名,此接收实体正授权访问初始实体。‘from’属                     性不应在初始实体发送到接收实体的流头中;然而,如果‘from’属性包括在内,它应当被接收实体忽略。       
                  3)        id—‘       id’属性应当仅用于从接收实体到初始实体的 XML 流头中。此属性是唯一一个由接收实体创建的,作为初始实体流与接收实体间会话的密钥,并且,在接收   应用        (通常是一个服务器)中是唯一的。注意:流 ID 可能是严格安全的,并且因此必须是即不能预测也不能重复的(参考[RANDOM]推荐关于随机安全观点)。id’属性不应在初始实体到接收实体的 XML 流头中;然而,如果‘id’属性包含在内,应被接收实体忽略。       
                  4)         xml:lang—‘       xml:lang’属性(定义在[XML]的 12.2)应当包含在初始实体的初始流头中,用于指定缺省语言,此语言可以是任何通过流发送的人类可读的        XML 字符数据。如果属性包含在内,接收实体应当记住此值并做为初始流与响应流的缺省值;如果此属性不包含在内,接收实体应当为两个流使用一个可配置的缺        省值,它必须为响应流在头中通信。对所有通过初始化流发送的节,如果初始实体不包含‘xml:lang’属性,接收实体应当应用缺省值;如果初始实体包含        ‘xml:lang’属性,接收实体不准修改或删除它(参考 xml:lang ( 9.1.5))。 ‘xml:lang’属性值必须是一个 NMTOKEN (定        义在[XML] ( 2.3)),并且必须与定义在 RFC3006[LANGTAGS]
中的格式一致。       
                  5)        version—版本属性出现设到至少是“1.0”信号值,(包括流特征)。有关代与属性处理的具体规则定义如下:       可总结如下:          



流错误       
           根流元素可能包含一个<error/>子元素,此元素由流命名空间前缀来加前缀。如果错误子元素感觉到一个流级别错误发生,它必须由一个兼容实体(通常是一个服务器而不是一个客户端)来发送。     


规则       
          以下规则应用于流级别错误:       
           1)        设想所有流级别错误均是不可恢复的;因此,如果一个错误在流级别层发生,那么检测错误的实体必须发送一个流错误给其它实体,发送一个关闭</stream>标记,并终止潜在的 TCP 连接。       
           2)         如果在流被建立期间发生错误,接收实体必须一直发送起始<stream>标记,将<error/>元素作为流元素的子元素,发送 关闭</stream>标记,并终止潜在的 TCP 连接。此种情况下,如果初始实体在‘to’属性(或根本没提供‘to’属性)中提供了一个未知主机,服务器应当在流头的‘from’属性中提供服务器的授权主机名,并在终止前发送。     


语法       
            流错误语法如下:       
                 

   <stream:error>       
                                    <defined-­‐condition       xmlns='urn:ietf:params:xml:ns:xmpp-­‐streams'/>       
                                    <text       xmlns='urn:ietf:params:xml:ns:xmpp-­‐streams'       
                                                                              xml:lang='langcode'>       
                                                  OPTIONAL       descriptive       text       
                                    </text>       
                                    [OPTIONAL       application-­‐specific       condition       element]       
                      </stream:error>      

<error/>元素:       
                         1)        必须包含一个子元素,此子元素与以下定义的已定义的节错误条件之一相一致;此元素必须被'urn:ietf:params:xml:ns:xmpp-­‐streams'命名空间认为是合的。   
                         2)        可能包含一个<text/>子元素,此子元素包含了更详细描述错误的 XML 字符数据;此元素必须被'urn:ietf:params:xml:ns:xmpp-­‐streams'命名空间认为是合格的,并且,应当拥有一个'xml:lang'属性来指明XML 字符数据的自然语言。       
                         3)        可能包含一个用于说明特殊应用错误条件的子元素;此元素必须由一个已定义应用命名空间来认证,并且,它的结构由那个命名空间来定义。      

<text/>元素是:

                         它可选的。如果包含了此元素,它应当仅用于提供描述性或诊断性的信息,来补充一个已定义的条件或特殊应用条件的意思;它不应当由一个应用以程序化的形式叙
述。它不应当作为错误消息展示给一个用户,但可能另外显示与包含条件元素(或元素们)相关的错误消息。     


已定义条件       
                        以下定义了流级别错误条件:       
                                1) <bad-­‐format/>-­‐-­‐已经发送 XML 的实体不能被处理;此错误可能用于代替更特殊的 XML相关错误,例如:<bad-­‐namespace-­‐prefix/>,<invalid-­‐xml/>,       
<restricted-­‐xml/>, <unsupported-­‐encoding/>,<xml-­‐not-­‐well-­‐formed/>,虽然更特殊的错误是首选。       
                                2)<bad-­‐namespace-­‐prefix/>-­‐-­‐实体已经发送了一个不被支持的名空间前缀,或在一个需要那样一个前缀的元素中发送了没有命名空间的前缀(参考 XML 命名空间名与前缀(11.2))。        
                                3)<conflict/>-­‐-­‐服务器正为实体关闭活动流,因为一个已经被初始化的新流与现存流冲突。       
                                4)<connection-­‐timeout/>-­‐-­‐一段时间内(可根据本地服务策略配置)实体并不通过流产生任何通信。       
                                5)<host-­‐gone/>-­‐-­‐由初始实体在流头中提供的‘to’属性值对应于一个主机名,而此主机名已不再被一个服务器当作主机了。       
                                6)<host-­‐unknown/>-­‐-­‐由初始实体在流头中提供的‘to’属性值于服务器所拥有的主机名不一致。    

                                7)<improper-­‐addressing/>-­‐-­‐一个在两个服务器间发送的节,缺少‘to’或‘from’属性(或此属性无值)       
                                8)<internal-­‐server-­‐error/>-­‐-­‐服务器经历了错误配置或其它未定义内部错误使其无法提供服务。      
                                9)<invalid-­‐from/>-­‐-­‐在‘from’地址中提供的 JID 或主机名与已授权的 JID 或有效域协商不匹配,此有效域协商为通过 SASL 或回叫服务器间的协商,或通过授权与资源绑定的客户端与服务器间的协商。       
                               10)<invalid-­‐id/>-­‐-­‐流 ID 或回叫 ID 是无效的或与以前提供的 ID 不匹配。       
                               11)<invalid-­‐namespace/>-­‐-­‐流命名空间名不只是 http://etherx.jabber.org/streams,或回叫命名空间名不只是"jabber:server:dialback"(参考 XML 命名空间名与前缀)       
                               12) <invalid-­‐xml/>-­‐-­‐实体通过流向执行验证的服务器发送了无效的 XML       
                               13)<not-­‐authorized/>-­‐-­‐实体试图在流被认证前发送数据,或不授权执行一个相关流协商的活动;接收实体在发送流错误前不准处理违规节。       
                               14)<policy-­‐violation/>-­‐-­‐实体违反了某些本地策略;服务器可能选择在<text/>元素或特殊-­‐应用条件元素中指定策略。       
                               15)<remote-­‐connection-­‐failed/>服务器不能适当的连接到远程实体,需要认证或授权。        
                               16)<resource-­‐constraint/>服务器缺少提供服务给流的必要的系统资源。       
                               17)<restricted-­‐xml/>实体试图发送受限的 XML 特征,例如评注、处理介绍,DTD,实体参考,或保留字符(参考(11.1))。       
                               18) <see-­‐other-­‐host/>服务器将不提供服务给初始实体,但正重定向传输给另一个主机;服务器应当指定替换的主机名或 IP 地址(必须是有效域标识符),作为<see-­‐other-­‐host/>元素的 XML 字符数据。       
                               19)<system-­‐shutdown/>服务器被关闭,并且所有的活动流被关闭。       
                               20)<undefined-­‐condition/>错误条件是由此列表中的其它已定义条件中的一个;此错误条件应当仅用在与特殊-­‐应用条件相结合。       
                               21)<unsupported-­‐encoding/>初始实体已在不被服务器支持的编码中为流编码(11.5)       
                               22)<unsupported-­‐stanza-­‐type/>初始实体已发送了一个不被服务器支持的第一级子流。       
                               23) <unsupported-­‐version/>由初始实体在流头提供的版本属性值指定了一个不被服务器支持的 XMPP 版本;服务器可能在<text/>元素中指定它支持的版本。     
                               24)<xml-­‐not-­‐well-­‐formed/>初始实体已经发送了不标准的 XML,标准的 XML 由[XML]定义。

示例

  <stream:error>	
   <xml-­‐not-­‐well-­‐formed	
   xmlns='urn:ietf:params:xml:ns:xmpp-­‐streams'/>	
   <text	
  xml:lang='en'	
  xmlns='urn:ietf:params:xml:ns:xmpp-­‐streams'>	
   Some	
  special	
  application	
  diagnostic	
  information!	  	
   </text>	
   <escape-­‐your-­‐data	
  xmlns='application-­‐ns'/>	  	
   </stream:error>	
   </stream:stream>	
 

范例:


此部分包含两个简化的客户端与服务器(“C”行是从客户端发送到服务器,而“S”行是由服务器发送到客户端)间基于流会话的例子;这些例子解释进一步的概念。

A basic "session":
C: <?xml version='1.0'?>
<stream:stream
to='example.com'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>

S: <?xml version='1.0'?>
<stream:stream
from='example.com'
id='someid'xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
...

C:
<message from='juliet@example.com'
to='romeo@example.net'
xml:lang='en'>
C:
<body>Art thou not Romeo, and a Montague?</body>
C: </message>

S: <message from='romeo@example.net'
to='juliet@example.com'
xml:lang='en'>
S:
S:
<body>Neither, fair saint, if either thee dislike.</body>
</message>

C: </stream:stream>
S: </stream:stream>

A "session" gone bad:


C: <?xml version='1.0'?>
<stream:stream
to='example.com'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>

S: <?xml version='1.0'?>
<stream:stream
from='example.com'
id='someid'
xmlns='jabber:client'xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>

C: <message xml:lang='en'>
<body>Bad XML, no closing body tag!
</message>
S: <stream:error>
<xml-not-well-formed
xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
</stream:error>
S: </stream:stream>


使用 TLS


概述
XMPP 包含一个方法,用于保护流不被篡改和偷听。此信道加密方法利用传输层安全(TLS)协议[TLS],连同“STARTTLS”扩展,在为描述在 RFC 2595[USINGTLS]中的
IMAP[IMAP],POP3[POP3],ACAP[ACAP]等相似协议扩展模型。用于 STARTTLS 扩展的命 名空间名是'urn:ietf:params:xml:ns:xmpp-tls'。一个给定域的管理者可能需要使用 TLS 来进行客户端到服务器的通信,服务器到服务器的通信,或二者兼有。客户端应使用 TLS 去保护流,在企图完成 SASL 协商之前,而且,服务器出于保护服务器到服务器的通信的考虑,应在两个域间使用 TLS。


应用以下规则:
1) 遵从此说明的初始实体必须包含版本属性,并在初始流头中将其值设为“1.0”。
2) 如果两服务器间的 TLS 协商发生,直到服务器宣称的域名系统(DNS)主机名被决定(参考服务器到服务器的通信(14.4))后,才能处理通信。
3) 当与此说明一致的接收实体收到一个包含版本属性设为至少“1.0”的初始化流时,发送一个流头作响应(包含版本标记)后,必须包含一 个<starttls/>元素(由
'urn:ietf:params:xml:ns:xmpp-tls'命名空间认证)并带有它所支持的其它 流特征的列表。[服务器以<starttls/>响应]
4) 如果初始实体选择使用 TLS,TLS 协商必须在 SASL 协商处理之前完成;这种协商顺序是必要的,用于帮助保护 SASL 协商期间发送认证信息,并在 TLS 协商之前这段时间,使基于使用认证的 SASL EXTERNAL 机制成为可能。
5) 在 TLS 协商期间,实体不准在根流元素中发送任何空白字符(匹配[XML]内容,产品[3])作为元素间(任何在 TLS 例子中的空白字符都只是为了便于阅读)的分隔符;这种限制有助于确保合适的安全层字节精度。
6) 接收实体必须考虑 TLS 协商在发送<proceed/>元素的关闭“>”字符之后立即开始。初始实体必须考虑 TLS 协商在收到来自于接收实体的<proceed/>元素的关闭“>”字符之后立即开始。
7) 初始实体必须验证由接收实体表示的证书;参考证书验证(14.2)相关证书验证步骤。

8) 证书必须根据初始实体(例如:一个用户)提供的主机名来检查,而不是通过域名系统解析的主机名;例如:如果用户指定一个"example.com"的主机 名,而 DNSSRV[SRV]查找并返回"im.example.com",证书必须作为"example.com"被检查。如果对任何此种 XMPP 实体(例如,客户 端或服务器)的一个 JID 在一个证书中被表示,它必须作为一个 UTF8String 来表示,UTF8String 在位于 subjiectAltName 中 的一个 otherName 实体中,使用[ASN.1]对象标识符"id-on-xmppAddr",在本文档 5.1.1 中说明。
9) 如果 TLS 协商成功,接收实体必须抛弃 TLS 生效之前,来自初始实体的任何非安全格式的知识。
10) 如果 TLS 协商成功,初始实体必须抛弃 TLS 生效之前,来自接收实体的任何非安全格式知识。
11) 如果 TLS 协商成功,接收实体不准提供 STARTTLS 扩展给当流重新开如时被提供的带有其他流特征的初始实体。
12) 如果 TLS 协商成功,初始实体必须继续 SASL 协商。
13) 如果 TLS 协商结果失败,接收实体必须终止 XML 流与潜在的 TCP 连接。
14) 参考强制实施技术(14.7)相关的必须被支持的机制。


ASN.1

用于 XMPP 地址的对象标识符上述[ASN.1]对象标识符"id-on-xmppAddr"定义如下:
id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
dod(6) internet(1) security(5) mechanisms(5) pkix(7) }
id-on
OBJECT IDENTIFIER ::= { id-pkix 8 }
id-on-xmppAddr
-- other name forms
OBJECT IDENTIFIER ::= { id-on 5 }
XmppAddr ::= UTF8String
对象标识符可能也以点分制显示,格式为"1.3.6.1.5.5.7.8.5"


叙述
当初始实体使用 TLS 保护一个带有接收实体的流时,步骤如下:
1) 初始实体打开一个 TCP 连接,靠发送开放 XML 流头给接收实体,此流头包含版本属性,并设其值至少为“1.0”,来初始化流。
2) 接收实体以打开一个 TCP 连接并发送一个 XML 流头给初始实体作为响应,此流头包含值至少为“1.0”版本属性。
3) 接收实体靠包含带有其它支持流特征(如果 TLS 需要与接收实体交互,它应当靠包含一个<required/>元素作为<starttls/>的子元素来标记此事实)的列表来为初始实体提供 STARTTLS 扩展。
4) 初始实体发起 STARTTLS 命令(例:由'urn:ietf:params:xml:ns:xmpp-tls' 命名空间确认的<starttls/>元素)去指导希望开始TLS 协商去保护流的接收实体。
5) 接收实体必须以由命名空间'urn:ietf:params:xml:ns:xmpp-tls'认证了的<proceed/>元素 或<failure/>元素响应。如果有失败情况发生,接收实体必须终止双方的 XML 流与潜在的 TCP 连接。如果接着向下进行,实体必须尝试 通过 TCP 连接完成 TLS 协商,并不准发送任何进一步的 XML 数据,直到 TLS 协商完成。
6) 初始实体与接收实体尝试依据[TLS]完成 TLS 协商。

7) 如果 TLS 协商不成功,接收实体必须终止 TCP 连接。如果 TLS 协商成功,初始实体必须靠发送一个开始 XML 流头给接收实体(它并不需要先发送一个关 闭</stream>标记,因为接收实体与初始实体必须考虑到原始流根据成功的 TLS 协商而被关闭),以初始一个新流。
8) 根据从初始实体接收的新流头,接收实体必须靠发送一个新 XML 流头给有可利用特征(不包括 STARTTLS 特征)的初始实体来响应。


示例

下面例子显示了一个客户端保护使用 STARTTLS(注:替换步骤显示在下一行,用来解释协议失败的情况;他们在本例中并不详尽也不是必须的由数据发送而触发)流的数
据流。


1 步:客户端初始流给服务器:

<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
to='example.com'
version='1.0'>

步 2:服务器以发送给客户端一个流标记作为响应:

<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
id='c2s_123'
from='example.com'
version='1.0'>

步 3:服务器发送 STARTTLS 扩展给客户端,并带有认证机制与任何其它流特征:

<stream:features>
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>
<required/>
</starttls>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
</mechanisms>
</stream:features>

步 4:客户端发送 STARTTLS 命令给服务器:
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>

步 5:服务器通知客户端它被允许处理

<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>

步 5(替代):服务器通知客户端 TLS 协商失败,并关闭流与 TCP 连接:

<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
</stream:stream>

步 6:客户端与服务器试图协商通过现存的 TCP 连接 完成 TLS 协商。
步 7:如果 TLS 协商成功,客户端初始化一个新流给服务器

<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
to='example.com'
version='1.0'>


步 7(代替 ):如果 TLS 协商不成功,服务器关闭 TCP 连接。
步 8:服务器靠发送带有任何可利用流特征的流头给客户端作为响应。


<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
from='example.com'
id='c2s_234'
version='1.0'>
<stream:features>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism><mechanism>EXTERNAL</mechanism>
</mechanisms>
</stream:features>

步 9:客户端继续 SASL 协商


使用 SASL


概述
XMPP 包含一个认证流的方法,此方法依靠一个简单认证与安全层(SASL)协议[SASL]的 XMPP-specific profile。SASL 提供一个一般化方法,用于给基于连接的协议加
认证支持,并且,XMPP 使用一个一般化 XML 命名空间 profile,用于 SASL,遵从[SASL]的profiling 需求。
以下规则应用:

1) 如果两个服务器间发生 SASL 协商,直到由服务器宣称的域名系统(DNS)主机名被解析了(参考服务器到服务器通信(14.4)),通信才可处理。
2) 如果实始实体能够进行 SASL 协商,,它必须在初始流头中包含值至少为“1.0”的版本属性。
3) 如果接收实体能够进行 SASL 协商,它必须在一个<mechanisms/>元素中广告一个或多个认证机制,此元素靠 'urn:ietf:params:xml:ns:xmpp-sasl'命名空间响应从初始实体(如果开放流标记包含所设值至少为“1.0”的版本属性) 接收的开放流标记认证。
4) 在 SASL 协商期间,实体不准在根流元素中发送任何空白字符(匹配[XML]内容,产品[3])作为元素间(任何在 SASL 例子中的空白字符都只是为了便于阅读)的分隔符;这种限制有助于确保合适的安全层字节精度。
5) 任何包含在 XML 元素中的 XML 字符数据,在 SASL 协商期间使用,必须使用 base64 编码,编码在 RFC3548 第三节有定义。
6) 如果所提供的一个“简单用户名”能够被选定 SASL 机制(例:由DIGEST-MD5 与 CRAM-MD5 机制所支持,但不靠 EXTERNAL 与 GSSAPI 机制所支持)所支持,在认证期间,初始实体应当作为简单用户名提供它的发送域(IP 地址或包含在域标识符中的全认证域名)在服务器对服务器的 通信情况下,或是它的已注册帐户名(包含在 XMPP 结点标识符中的用户或结点名)在客户到服务器的通信情况下。
7) 如果初始实体希望代表其它实体与支持授权身份传输的被选 SASL 机制来行动,初始实体在 SASL 协商期间必须提供一个授权身份。如果初始实体不希望代表另 一个
实体行动,它不准提供一个授权身份。正如[SASL]中指定的,初始实体不准提供一个授权身份,除非一个授权身份不同于缺省授权身份,此缺省授权身份 派生于描述在[SASL]中的认
证身份。如果提供了,授权身份值对服务器来说必须是<domain>值形式(例:只有一个域标识符),对客户 端来说,必须是<node@domain>值形式(例:结点标识符与域标识符)。
8) 靠涉及到安全层协商的 SASL 协商的成功,接收实体必须抛弃来自本身没有获得 SASL 协商的初始实体的任何知识。
9) 靠涉及到安全层协商的 SASL 协商的成功,初始实体必须抛弃来自本身没有获得 SASL 协商的接收实体的任何知识。
10) 参考必须被支持的相关机制的强制实施技术


叙述
当初始实体使用 SASL 认证接收实体时,步骤如下:
1) 初始实体请求 SASL 认证,通过在开放 XML 流头中包含版本属性,并将其发送给接收实体,属性值设为“1.0”。
2) 发送一个 XML 流头作为回应后,接收实体广告一个可利用的 SASL 认证机制列表;列表中每一项都是一个<mechanism/>元素,作 为<mechanism/>容器元素的子元素,'urn:ietf:params:xml:ns:xmpp-sasl'命名空间认证,在流 命名空间中,依次是<features/>元素的子元素。如果使用 TLS(5)需要在一个特别认证机制可能使用之间建立,
接收实体不准提供在 TLS 协商之前的可利用 SASL 认证机制列表中的机制。如果初始实体在TLS 协商之前出示了有效证书,接收实体应当在 SASL 协商(参考[SASL])之 间,提供 SASLEXTERNAL 机制给初始实体,虽然 EXTERNAL 机制可能在其它环境下被提供了。
3) 初始实体选择一个机制,靠发送一个已被'urn:ietf:params:xml:ns:xmpp-sasl'命名空间认定为合格 的<auth/>元素给接收实体,并为‘mechanism’属性包含一个合适的值。如果此机制需要 XML 字符数据,此元素可能包含XML 字 符数据(在 SASL 术语中,“初始响应”);如果初始实体需要发送一个 0 长度的初始响应,它必须按一个单等号符号(“=”)传输此响应,意味着响应出现, 但不包含数据。

4) 如果需要,接收实体靠发送一个<chanllenge/>元素来挑战实始实体,此元素由给初始实体的 'urn:ietf:params:xml:ns:xmpp-sasl'命名空间来限定;此元素可能包含 XML 字符数据(必须根据由初始实体选择的 SASL 机制的定义一致的来计算)。
5) 初始实体响应此挑战,靠发送由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<response/> 元素给接收实体;此元素可能包含 XML 字符数据(必须根据由初始实体选择的 SASL 机制的定义一致的来计算)。
6) 如果需要,接收实体发送更多的挑战,初始实体发送更多的响应。


Challenge/response 序列对继续,直到以下三种事情之一发生:
1) 初始实体终止握手,靠发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<abort/>元素 给接收实体。根据接收一个<abort/>元素,接收实体应当允
许一个可配置的但合理的重试号(至少 2),之后,必须终止 TCP 连接;这使初 始实体(例:一个终端用户客户端)能够忍受已提供的不正确的信任(例:一个错误类型的 password)
而不用被迫重连。
2) 接收实体报告握手失败,靠发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定 的<failure/>元素给初始实体(失败的特殊原因应当以<failure/>元素的子元素进行通 信,<failure/>元素定义在 SASL 错误中
(6.4))。如果错误情况发生,接收实体应当允许一个可配置的,但合理的重试号(至少 2),之后,它必须终止 TCP 连接;这使初始实体(例:一个终端用户客户端)能够忍受已提供的不正确的信任(例:一个错误类型的 password)而不用 被迫重连。
3) 接收实体报告握手成功,靠发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定 的<success/>元素给初始实体;此元素可能包含 XML 字符数据(在 SASL 术语中,“additional data with success”),如果需要靠选定的 SASL 机制。根据接收的<success/>元素,初始实体必须靠发送一个开放的 XML流头去初始化 一个新流给接收实体(它不必事先发送一个关闭</stream>标记,因为接收实体与初始实体必须考虑源流根据发送或接 收<success/>元素而将被关闭)。根据从初始实体接收的新流头,接收实体必须发送一个新 XML 流头给初始实体作为响应,并带有任何可 利用的特征(但并不包含 STARTTLS 与 SASL 特征)或一个空<features/>元素(重要表示没有其它特征可利用);任何那种其它 在此未定义的特征必须由 XMPP 的相关扩展来定义。


SASL 定义
[SASL]的 profiling 需求要求协议定义提供以下信息:
服务名:“xmpp”
初始序列:初始实体提供一个开放 XML 流头后,并且接收实体按此响应后,
接收实体提供一个可接收的认证方法列表。初始实体从列表中选择一个方法并作为
‘machanism’属性值发送给接收实体,此属性被<auth/>元素拥有,随意的包括一个初始响
应以避免环路。
交换序列:挑战与响应通过由接收实体到初始实体<challenge/>元素的交换
与由初始实体到接收实体的<response />元素的交换而执行。接收实体靠发送一个
<failure/>元素报告错误,发送一个<success/>元素报告成 功;初始实体靠发送<abort/>
元素终止交换。根据成功协商,两端都认为源 XML 流将被关并且新流头由两端实体发送。
安全层协商:安全层在为接收实体发送<success/>元素的关闭“>”字符后立
即有效,安全层在为初始实体发 送<success/>元素的关闭“>”字符后立即有效。层顺序为:
首先是[TCP],然后是[TLS],然后是[SASL],然后是 XMPP。
使用授权身份:授权身份可以被 XMPP 用于指示客户端非缺省<node@domain>
或服务器发送<domain>。


SASL 错误
以下是 SASL 相关错误条件的定义:(略)
1)<aborted/>--
2)<incorrect-encoding/>--
3)<invalid-authzid/>--
4)<invalid-mechanism/>--
5)<mechanism-too-weak/>--
6)<not-authorized/>--
7)<temporary-auth-failure/>--


例子

以下例子显示了使用 SASL 授权的客户端与服务器端的数据流,正常情况下,
是在 TLS 协商(注:显示在下面的替换步骤用于显示错误情况的协议;他们并不详尽也不是必要的由本例中数据发送而触发。)成功之后。


步 1:客户端初始流给服务器:
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
to='example.com'
version='1.0'>
步 2:服务器使用一个流标记作为响应发送给客户端:
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
id='c2s_234'
from='example.com'
version='1.0'>
步 3:服务器通知客户端可利用的认证机制:
<stream:features>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
</mechanisms>
</stream:features>
步 4:客户端选择一个认证机制:
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'
mechanism='DIGEST-MD5'/>

步 5:服务器发送一个[BASE64]编码挑战给客户端:
<challenge
xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9I
k9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz
Cg==</challenge>
解码挑战是:
realm="somerealm",nonce="OA6MG9tEQGm2hh",\
qop="auth",charset=utf-8,algorithm=md5-sess
步 5(替换):服务器返回错误给客户端:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<incorrect-encoding/>
</failure>
</stream:stream>
步 6:客户端发送一个[BASE64]编码响应挑战:
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i
T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw
MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i
QzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNoYXJzZXQ9dXRmLTgK
</response>

步 7:服务器发送另一个[BASE64]编码挑战给客户端:
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=
</challenge>
解码挑战是:
rspauth=ea40f60335c427b5527b84dbabcdfffd
步 7(替换):服务器返回错误给客户端:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<temporary-auth-failure/>
</failure>
</stream:stream>
步 8:客户端响应挑战:
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>

步 9:服务器通知客户端认证成功:
LHJlc3BvbnNlPW<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
步 9(替换):服务器通知客户端认证失败:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<temporary-auth-failure/>
</failure>
</stream:stream>
步 10:客户端初始化一个新流给服务器:
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
to='example.com'
version='1.0'>
步 11:服务器通过发送流头来响应客户端,伴随有任意另外的特征(或空特征元素):
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
id='c2s_345'
from='example.com'
version='1.0'>
<stream:features>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
</stream:features>
服务器到服务器的例子 可以自己查看文档!:)



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值