基於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='[email protected]'
to='[email protected]'
xml:lang='en'>
C:
<body>Art thou not Romeo, and a Montague?</body>
C: </message>

S: <message from='[email protected]'
to='[email protected]'
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>
服務器到服務器的例子 可以自己查看文檔!:)



發佈了71 篇原創文章 · 獲贊 106 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章