MSNP18協議分析(二)--- MSN登錄身份認證

這一篇開始主要介紹MSN登錄部分的協議分析,總體來說,登陸這一塊是整個MSNP協議的一大塊,也是比較複雜的一部分。整個登錄的過程主要包括:連接服務器,身份驗證,獲取用戶信息和聯繫人列表,把聯繫人列表發送給服務器,發送個人信息和狀態,上線通知。我也打算按照登錄的順序去進行介紹。

這裏我主要關注的是如何成功的登錄,但是因爲大多數都是抓包,也沒有權威的官方資料參考,所以不能保證完全正確和詳細,有些也不能給出合理的解釋,但還是儘可能的介紹登錄中會出現的各種情況,以及登錄中用到的命令。

在使用命令之前,我們需要注意的時,普通命令後面都帶有/r/n結尾。而palyload命令是根據數據長度找到結尾。我們在發送時必須注意這一點,如果發送的數據沒有結尾標識,服務器會斷開連接。另外發送的命令都需要以UTF8格式發送。


.

一 連接服務器

登錄最終的目的是登錄到NS服務器,與之建立連接並進行交互。一直到用戶退出或註銷時,才斷開與服務器的連接。在登錄到NS服務器之前,我們需要先連接到DS服務,並獲得NS服務器的地址。

.

1 連接到DS服務器

DS服務器的地址是:messenger.hotmail.com ,端口號是1863在通過Socke連接之前需要對這個域名進行解析,而且解析的IP是不固定的,所以不建議使用固定的IP去連接DS服務器。以下是MSN Live 2009登錄時和DS服務器之間的交互數據。

>>>VER 1 MSNP18 MSNP17 CVR0/r/n

<<<VER 1 MSNP18/r/n

>>>CVR 2 0x0804 winnt 5.1 i386 MSNMSGR 14.0.8089.0726 msmsgs[email protected]/r/n

>>>USR 3 SSO I[email protected]/r/n

<<<CVR 2 14.0.8089 14.0.8089 14.0.8089 http://msgruser.dlservice.microsoft.com/download/0/9/7/0974F7CD-D082-46FE-922D-806670345793/zh-chs/wlsetup-cvr.exehttp://download.live.com/?sku=messenger/r/n

<<<XFR 3 NS 207.46.124.241:1863 U D/r/n

VER命令:

這是和DS服務器發送的第一條消息,他是告訴服務器客戶端支持的MSNP版本。MSNP版本功能在上一篇文章有所介紹,發送的版本號可以是多個版本。發送的版本號是區分大小寫的。最後一個參數是CVR0,他實際是(CVQ, VER) 的命令集合,用來設置客戶端版本,使得客戶端能夠升級。但具體作用不詳,不發送也是可以的。最後要注意的是最後一個參數結尾要帶上"/r/n"。服務器在接受到命令後,會從我們發送的協議版本中選一個他支持的最高本版,如果我們發送的協議版本他不支持,就返回一個0。目前最新的協議支持到了MSNP21。

.

CVR命令:

這是是CVR命令的格式CVR trid lcid osName osVersion processorType clientName clientVersion [brandId] [userHandle]。聰從名字就能看出每個參數的作用。 lcid是地區編碼,0x804表示的是中國。 clientVersion我們可以根據MSNP對應的官方版本來確定; brandID官方客戶端是msmsgs ,最後一個就是登陸用戶的賬號。服務器會返回給你版本信息,最新的MSN下載地址,這些可以無視,我們又不是用MS的客戶端。哈

.

USR命令:

USR命令是在登錄過程中用來身份驗證的一個命令,會多次使用,在連接DS服務器時比較簡單。第一個參數是SSO,表示身份驗證方式。從MSNP15開始就是用此方法驗證,而之前的版本使用TWN。所以要支持多個協議方式登陸,首先要區分身份驗證的方式。第二個參數是 (I = Initiate, S = Respond to Challenge). 我們這裏第一次使用,發送I,最後一個參數還是用戶的賬號。USR還有多種用法,後面用到時繼續介紹。

.

XFR命令:

服務器返回的XFR命令的作用是通知你NS服務器的地址,另一個作用就是在聊天時返回給你一個SB服務器的地址。第一個參數NS表示這是發送的NS地址,第二個參數就是服務器的IP地址和端口號。最後的U D表示什麼不知道。接受到XFR後,DS服務器會自動斷開SOCKET連接。這裏XFR是否會不返回NS地址,不太清楚,起碼目前沒有發生過,當然程序中還是需要對這種情況進行處理。

.

以上就是連接DS服務的全部過程,介紹了幾個命令的用法。要注意的是,這裏並沒有太多的介紹可能返回的錯誤。比如你已經登陸了,在發送這裏的USR命令就會出錯;如果發送消息過於頻繁也會出錯等等。但是一般,我們不需要太過關注這些,而且錯誤情況太多也根本關注不過來。後面我會給出所有服務器返回的錯誤代碼的列表,實際出現問題時在去解決。這裏我們只關注登錄過程。需要注意的是發送命令不一定需要發送一條,接受一條才能在發送一條。有些命令可以不需要等待上一條返回就發送的。

.

2 連接到NS服務器

從XFR獲得到了NS服務器的IP地址很端口號以後,我們就能使用SOCKET連接到NS服務器了。

>>>VER 1 MSNP18 MSNP17 CVR0/r/n


>>>CVR 2 0x0804 winnt 5.1 i386 MSNMSGR 14.0.8089.0726 msmsgs [email protected]/r/n


>>>USR 3 SSO I [email protected]/r/n

<<<VER 1 MSNP18/r/n


<<<CVR 2 14.0.8089 14.0.8089 14.0.8089/r/n http://msgruser.dlservice.microsoft.com/download/0/9/7/0974F7CD-D082-46FE-922D-806670345793/zh-chs/wlsetup-cvr.exe http://download.live.com/?sku=messenger


<<<GCF 0 5521/r/n<Policies>省略</Policies>

<<<USR 3 SSO S MBI_KEY_OLD kVJy53UCBCupSu09gfE9rH47mtDGzmINva5/vEiYJIfxft+an0igQBC445kNyIkV/r/n

以上是MSN 2009連接NS服務時發送的前3條命令,我們可以看到這裏沒有等待上一條返回,而是一起發送了。這裏的3條命令和DS服務器發送的一樣,這裏就不在過冬介紹了。但是發現我們發送的USR命令後,返回的和DS時不一樣了。

GCF命令:

此命令是從服務器獲得配置文件信息,可以看他是一個playLoad命令,命令後面是ID好和數據長度。數據是使用XML存放。 但是從MSNP13開始此命令已經不在使用,所以我麼可以不處理這一條命令。

.

USR命令:

在前面我們發送過USR命令,在這裏我們收到了服務器發送來的USR命令。這裏我們要關注的是MBI_KEY_OLD 已經後面的一傳值。這個就是SSO認證過程中需要用到的KEY。這裏返回的類型可能是MBI、MBI_SSL 、MBI_KEY_OLD等類型。

.

3 斷開服務器連接

斷開服務器連接有多種方式,對於NS服務器,我們不需要主動斷開,在獲得XFR之後,服務器會主動斷開SOCKET連接。對於NS服務器,我們退出時可以發送OUT命令,這個命令不需要ID和任何參數,服務器接收到OUT之後就會斷開連接,這是一種比較好的斷開方法。對於SB服務器一樣,我們也只需要發送一個OUT命令。

我們還可以直接斷開客戶端的SOCKET連接,但是這樣服務器不一定能馬上改變我們的狀態,好友也不一定馬上能看到我們下線或退出對話。而如果我們發送的命令不正確,有些情況,服務求就會馬上斷開連接。有一些錯誤服務器在斷開之前會返回錯誤代碼。

.

OUT命令:

我們能給服務器發送OUT表示我們退出,服務器有時也會在斷開我們之前發送OUT通知我們。OUT [Reason]這是服務器發送OUT給我們時候的格式。當收到的與原因是OTH時,表示我們被其他登陸點註銷,這可能是其他登陸點不支持多點登陸,也可能是被其他登陸點選擇註銷;當收到原因是RCT <delay> 的時候,表示服務器斷開,指定的時間之後可以進行重連;SSD則表示服務器掛了;TME則表示登陸的終端太多,我們被服務器kick out了。我們可以根據不同情況進行處理。

.

UUN命令:UUN trid receiverHandle[;EPID] appID size/r/nBODY

從MSNP16之後,開始支持多點登陸,我們可以註銷掉其他登陸點的MSN。使用的命令就是UUN。這個命令作用是發送客戶端1對1的通知消息。使用這個命令註銷其他登陸點只是其中的一個功能。第一個參數要通知的客戶端用戶的賬號,第二個參數是EPID,這個是可選的。支持多點登陸的版本,每個登陸點會有一個GUID來表示終端。第三個參數是操作類型ID,我們註銷其他登陸點使用4;最後是發送BODY的大小;註銷其他終端,官方服務發送的是大小爲14的一串字符ngoawyplzthxbye,沒有嘗試能否使用其他字符,我認爲應該是可以的。服務器接受到這個消息後,會根據EPID向其他客戶端發送OUT OTH命令。注意發送EPID時不要少了前面的分號。 發送成功服務器會返回UUN trid OK。

App ID Description
0 Debug App ID
1 2-share
2 Voice conversation invites (never sent in UUN, only UBN)
3 P2P bootstrapping
4 Log off my remote endpoints
5 Closing the conversation window
6 Contact List Update
7 Clear RL Prompt
8 Forward non-session message (i.e. Offline IM)
9 RESERVED
10 TURN bridge notification
11 Meta-data channel for audio calls
12 Signaling channel for Voice over MSNP

以上是在UUN命令中使用到的AppID編號,同樣這個編號也在UBN命令中使用。

UBN命令:UBN senderHandle;EPID appID size/r/nBody (>=MSNP 16)

在MSNP小於16的版本中,沒有EPID這一項目。而這個命令從MSNP13開始支持。和UUN一樣,他們的body內容最大長度爲6144個字節,使用ASCII編碼。這個命令接受到的可能是其他終端對UUN的返回結果,也可能是服務器發送來的邀請。

.

.

二 身份驗證

從MSNP15開始,微軟採用了新的身份認證方式SSO(Single Sign-On)。在MSN整個使用過程中,並不僅僅是登錄需要身份驗證,我們從WebService取得聯繫人列表,個人信息,用戶頭像,離線消息等等都需要身份驗證。於是在登錄的時候,我們進行身份驗證的請求,服務會發送給我們一些列的ticket,他們對應了不同的服務請求。所以在開始登陸之前我們必須進行SSO認證,獲得所需的ticket。SSO認證是通過WebServices服務完成的,SSO認證的地址爲:https://login.live.com/RST.srf ,需要注意的是對於msn。com結尾的賬號,需要提交到 https://msnia.login.live.com/pp550/RST.srf 進行認證。目前好像無法註冊msn.com,也沒有可用的賬號,所以無法測試。

.

1 SSO認證方式

  1. <Envelopexmlns="http://schemas.xmlsoap.org/soap/envelope/"
  2. xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
  3. xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
  4. xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
  5. xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
  6. xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
  7. xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
  8. xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
  9. <Header>
  10. <ps:AuthInfo
  11. xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL"
  12. Id="PPAuthInfo">
  13. <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
  14. <ps:BinaryVersion>4</ps:BinaryVersion>
  15. <ps:UIVersion>1</ps:UIVersion>
  16. <ps:Cookies></ps:Cookies>
  17. <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
  18. </ps:AuthInfo>
  19. <wsse:Security>
  20. <wsse:UsernameTokenId="user">
  21. <wsse:Username>[email protected]</wsse:Username>
  22. <wsse:Password>PasswordGoesHere</wsse:Password>
  23. </wsse:UsernameToken>
  24. </wsse:Security>
  25. </Header>
  26. <Body>
  27. <ps:RequestMultipleSecurityTokens
  28. xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL"
  29. Id="RSTS">
  30. <wst:RequestSecurityTokenId="RST0">
  31. <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
  32. <wsp:AppliesTo>
  33. <wsa:EndpointReference>
  34. <wsa:Address>http://Passport.NET/tb</wsa:Address>
  35. </wsa:EndpointReference>
  36. </wsp:AppliesTo>
  37. </wst:RequestSecurityToken>
  38. <wst:RequestSecurityTokenId="RSTn">
  39. <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
  40. <wsp:AppliesTo>
  41. <wsa:EndpointReference>
  42. <wsa:Address>domain</wsa:Address>
  43. </wsa:EndpointReference>
  44. </wsp:AppliesTo>
  45. <wsse:PolicyReferenceURI="policy parameter"></wsse:PolicyReference>
  46. </wst:RequestSecurityToken>
  47. ...
  48. ...
  49. </ps:RequestMultipleSecurityTokens>
  50. </Body>
  51. </Envelope>

以上XML就是我們要提交到WebService的SOAP格式。首先我們需要填充自己的賬號和密碼到XML中。我們看到Body中有<wst:RequestSecurityToken Id="RSTn">...</wst:RequestSecurityToken>這樣一個塊,每一個代表一個你想要進行身份驗證的域。RST0是必須的,我們添加對應的身份認證時,只需要構造從RST1開始節點塊。這樣你可以只提交一次,就獲得多個不同的域的身份認證。 下面列出了所有域的相關信息。

RST Dimain域 Policy Ref Purpose
RST0 http://Passport.NET/tb 作用不明,但是必須包含才能請求成功
RST1 messengerclear.live.com NS USR命令獲得的 Authentication for messenger.
目前在登錄時使用
RST2 messenger.msn.com ?id=507 Messenger website authentication。
目前在獲得離線消息時使用
RST3 contacts.msn.com MBI (used in WLM 8.5.1288.816) Authentication for the Contact server.
目前在操作聯繫人時使用
RST4 messengersecure.live.com MBI_SSL 未知
RST5 spaces.msn.com spaces.live.com MBI Authentication for the Windows Live Spaces
應該是登錄到空間使用
RST6 livecontacts.live.com MBI Live Contacts API, a simplified version of the Contacts SOAP service
目前沒有使用到
RST7 storage.live.com MBI Storage REST API
目前獲取用戶頭像時使用

我們只需要根據上表的內容,填充自己需要的域節點的內容,提交到服務器,就可以獲得對應服務的ticket。RTS1的Policy Ref是我們前面USR命令所獲得的MBI_KEY_OLD,前面說過了,這個參數可能有MBI、MBI_SSL等類型,所以要根據USR返回值填如XML中。網上有些地方是在NS連接前去進行SSO認證,這個是不正確的,因爲我們還沒獲得Policy Ref。雖然目前基本發送的都是MBI_KEY_OLD。然後服務器會返回給我們如下的XML:

  1. <S:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  2. <S:Header>
  3. There is really data here, butfor space it has be removed
  4. </S:Header>
  5. <S:Body>
  6. <wst:RequestSecurityTokenResponseCollection
  7. xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
  8. xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust"
  9. xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
  10. xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
  11. xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
  12. xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
  13. xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault">
  14. <wst:RequestSecurityTokenResponse>
  15. <wst:TokenType>urn:passport:legacy</wst:TokenType>
  16. <wsp:AppliesTo xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing">
  17. <wsa:EndpointReference>
  18. <wsa:Address>http://Passport.NET/tb</wsa:Address>
  19. </wsa:EndpointReference>
  20. </wsp:AppliesTo>
  21. <wst:LifeTime>
  22. <wsu:Created>2006-12-06T05:12:10Z</wsu:Created>
  23. <wsu:Expires>2006-12-07T05:12:10Z</wsu:Expires>
  24. </wst:LifeTime>
  25. <wst:RequestedSecurityToken>
  26. <EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#"
  27. Id="BinaryDAToken0"
  28. Type="http://www.w3.org/2001/04/xmlenc#Element">
  29. <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc">
  30. </EncryptionMethod>
  31. <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  32. <ds:KeyName>http://Passport.NET/STS</ds:KeyName>
  33. </ds:KeyInfo>
  34. <CipherData>
  35. <CipherValue>
  36. cipher data you don't need to worry about
  37. </CipherValue>
  38. </CipherData>
  39. </EncryptedData>
  40. </wst:RequestedSecurityToken>
  41. <wst:RequestedTokenReference>
  42. <wsse:KeyIdentifier ValueType="urn:passport"></wsse:KeyIdentifier>
  43. <wsse:Reference URI="#BinaryDAToken0"></wsse:Reference>
  44. </wst:RequestedTokenReference>
  45. <wst:RequestedProofToken>
  46. <wst:BinarySecret>ignore this one</wst:BinarySecret>
  47. </wst:RequestedProofToken>
  48. </wst:RequestSecurityTokenResponse>
  49. <wst:RequestSecurityTokenResponse>
  50. <wst:TokenType>urn:passport:compact</wst:TokenType>
  51. <wsp:AppliesTo xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing">
  52. <wsa:EndpointReference>
  53. <wsa:Address>messengerclear.live.com</wsa:Address>
  54. </wsa:EndpointReference>
  55. </wsp:AppliesTo>
  56. <wst:LifeTime>
  57. <wsu:Created>2006-12-06T05:12:10Z</wsu:Created>
  58. <wsu:Expires>2006-12-06T13:12:10Z</wsu:Expires>
  59. </wst:LifeTime>
  60. <wst:RequestedSecurityToken>
  61. <wsse:BinarySecurityToken Id="Compactn">
  62. t=<ticket goes here>&p=
  63. </wsse:BinarySecurityToken>
  64. </wst:RequestedSecurityToken>
  65. <wst:RequestedTokenReference>
  66. <wsse:KeyIdentifier ValueType="urn:passport:compact"></wsse:KeyIdentifier>
  67. <wsse:Reference URI="#Compactn"></wsse:Reference>
  68. </wst:RequestedTokenReference>
  69. <wst:RequestedProofToken>
  70. <wst:BinarySecret>binary secret (you need this)</wst:BinarySecret>
  71. </wst:RequestedProofToken>
  72. </wst:RequestSecurityTokenResponse>
  73. <wst:RequestSecurityTokenResponse>
  74. <wst:TokenType>urn:passport:legacy</wst:TokenType>
  75. <wsp:AppliesTo xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing">
  76. <wsa:EndpointReference>
  77. <wsa:Address>site domain</wsa:Address>
  78. </wsa:EndpointReference>
  79. </wsp:AppliesTo>
  80. <wst:LifeTime>
  81. <wsu:Created>2006-12-06T05:12:10Z</wsu:Created>
  82. <wsu:Expires>2006-12-06T05:20:30Z</wsu:Expires>
  83. </wst:LifeTime>
  84. <wst:RequestedSecurityToken>
  85. <wsse:BinarySecurityToken Id="PPTokenn">
  86. t=<site ticket here>&p=<site profile here>
  87. </wsse:BinarySecurityToken>
  88. </wst:RequestedSecurityToken>
  89. <wst:RequestedTokenReference>
  90. <wsse:KeyIdentifier ValueType="urn:passport"></wsse:KeyIdentifier>
  91. <wsse:Reference URI="#PPTokenn"></wsse:Reference>
  92. </wst:RequestedTokenReference>
  93. </wst:RequestSecurityTokenResponse>
  94. ...
  95. ...
  96. </wst:RequestSecurityTokenResponseCollection>
  97. </S:Body>
  98. </S:Envelope>

我們發送的XML中請求了幾個認證的域,服務求就會返回對應的<wst:RequestSecurityTokenResponse>節點。其中<wsse:BinarySecurityToken Id="Compactn"> 和 RSTn 中的數字n對應,其節點值t就是對用的認證使用的ticket<wsse:BinarySecurityToken Id="PPTokenn">節點中包含ticket和profile,因爲對用的服務需要使用到這兩個值,目前我們在獲取離線消息時需要使用。如果我們驗證時的賬戶和密碼不正確時,返回的XML會包含wsse:FailedAuthentication

至此我們已經完成了SSO認證的過程,我們可以把ticket 、profile保存下來,供後面使用,這裏要注意,返回的XML中包含ticke的過期時間,如果過期了,必須從新進行請求。

.

2 登錄驗證

前面我們已經獲得所有需要的ticket,當然登錄需要的ticket也獲得了。另外我們還需要從保存登錄ticket節點下<wst:BinarySecret>節點中的值,我們叫做BinarySecret。以下是在完成SSO認證之後,進行的登錄身份驗證發送的命令。

<<<USR 3 SSO S MBI_KEY_OLD kVJy53UCBCupSu09gfE9rH47mtDGzmINva5/vEiYJIfxft+an0igQBC445kNyIkV/r/n

>>SSO認證---webservices

<<<獲得各個服務的ticket

>>>USR 4 SSO S t=EwBoAswbAQAUs1/VcBU2sH7mwYy3BysWZ71CRDGAAP2ngxo5hiNLK0FzCGY1llPy8F5Uv8GQVTL2FDWo0UFZ2P4kDFk95WWhFl4ydSN8zJQVpzq5YlhSaTJc/JziNMZV0RBOaNDv0yuuGKPZ7gkaHLF5QDF5t0xChHLupla0+WYt5N3rnfRjU8QnYqgvkdMtEolkfInY0lxyyfmBSVeSA2YAAAhynJ+hLqBEy7gBZf6U97YpNCZdVsu8Uwr5GMs0FY8aWocMlSKU0V3rBP4igonwRPb/VLYGAztVZxFHZx/abNHOt/Sh83M7LWhqvPPbcWKuOIcevMzwParNhKZ2VWS1TymnQuqnA8igbYrDtAi1QcD6iEoQy/sM4cb7ryM/MjUZCbWQ6xg5CecLNlZT5onfqZ2IJJSZjrRO/9ZEU0rpH7N8nj/ycJVgiKJR8emWDec7eh1CbFltxtFz3ZCnWcwU+GCqhTU7IsTLrX6LYxjSTUm4eG8x19mxbJpjEv1Zpgqajdwfobu4FzKe0yF64hnVJd2Kv8hqnTq38XtcocSNexs/Sue4SknaOMG2u69Yu7b6CD/Ih+BOu4V2ZKn/EndfcW7tC7jh0x8qdTzy7sjJqQuz6sGMCr1uEt3Lx7tlT7nj+RDW5MG9MEMFid4ordfe2R1DV4bKB3D4xZWc7gT4rjfTcxtlRnrP6kOM6n9hPDScewrj4fknSzW1OKd8yCEtdQjb4pnvvmWRWqDO1ImQPLcK5Q3CqYb0BGf53w3fL/FEoWD3m0drktFlK3nGW4F3smsfdOkFGnYF/zSg7Jg9erW7Z8LlAQ==&p= HAAAAAEAAAADZgAABIAAAAgAAAAUAAAASAAAAAAAAAAAAAAAoHfdK2pjBY1wLTHXDzh9VtaqYOb2g4n+epmErFlCejBt2pMDFMNR4fFjknY6XsftT1qboX3K45VNK3ICTjEbPsvR1Sog3D14Rz2Q6JPKFrEAWZ23PEjCV6f8XJw= {95DC3D4B-7FBF-46B5-B670-FE0E75B89217}

<<<USR 4 OK [email protected] 1 0

以上繼續使用USR SSO S命令,最後一個參數的格式是t=...&p=...{epid} , 這裏的t就是我們SSO中取得和登錄有關的ticket,而p是由USR 3中那一長串nonce和上面取得的BinarySecret通過計算得到的,具體計算方法後面介紹,最後一個參數是客戶端的ID,是一個GUID,用來唯一標識一個終端。如果重複,會導致之前登錄的終端被踢下線。

發送此條命令之後,如果驗證通過,服務器會發送USR OK通知客戶端登錄成果,如果發送的t和p不對,會返回錯誤代碼:911 ERR_AUTHENTICATION_FAILED。

.

3 生成登錄是發送的p

上面的USR 4命令中包含了p,這個p是由USR 3中得到的nonce和webservice請求獲得的BinarySecret計算得到P。在介紹算法之前,我們需要一個結構體,我們最終的p就是這個結構體的值,發送給服務器。

  1. typedefstruct tagMSGRUSRKEY
  2. {
  3. /** 28. Does not count data */
  4. unsigned int uStructHeaderSize;
  5. /** CRYPT_MODE_CBC (1) */
  6. unsigned int uCryptMode;
  7. /** TripleDES (0x6603) */
  8. unsigned int uCipherType;
  9. /** SHA1 (0x8004) */
  10. unsigned int uHashType;
  11. /** 8 */
  12. unsigned int uIVLen;
  13. /** 20 */
  14. unsigned int uHashLen;
  15. /** 72 */
  16. unsigned int uCipherLen;
  17. /** IV Data */
  18. unsigned char aIVBytes[8];
  19. /** Hash Data */
  20. unsigned char aHashBytes[20];
  21. /** Cipher Date */
  22. unsigned char aCipherBytes[72];
  23. } MSGUSRKEY;

算法是按一下步驟來的:http://msnpiki.msnfanatic.com/index.php/MSNP15:SSO#Computing_the_return_value

1. 對獲得的BinarySecret進行BASE64編碼,存放到key1中

2.使用SHA_HMAC加密key1,按一下方法進行

hash1 = SHA1-HMAC(key1,"WS-SecureConversationSESSION KEY HASH")
hash2 = SHA1-HMAC(key1,hash1+"WS-SecureConversationSESSION KEY HASH")
hash3 = SHA1-HMAC(key1,hash1)
hash4 = SHA1-HMAC(key1,hash3+"WS-SecureConversationSESSION KEY HASH")
然後把hash的20個字節,以及hash的前4個字節組成一個24字節的字符串存入 key2.

3.使用SHA_HMAC加密key2,按一下方法進行

hash1 = SHA1-HMAC(key2,"WS-SecureConversationSESSION KEY ENCRYPTION")
hash2 = SHA1-HMAC(key2,hash1+"WS-SecureConversationSESSION KEY ENCRYPTION")
hash3 = SHA1-HMAC(key2,hash1)
hash4 = SHA1-HMAC(key2,hash3+"WS-SecureConversationSESSION KEY ENCRYPTION")
然後把hash的20個字節,以及hash的前4個字節組成一個24字節的字符串存入 key3。

4. 使用SHA_HMAC加密key2和前面得到的nonoc,存入hash = SHA1-HMAC(key2, nonce)

5. 官方客戶端會在nonce的後面追加8個字節的16進制數08

6. 創建8個字節隨機數data

7. 使用TripleDes algorithm算法。模式設置爲CBC,將上面的key3,隨機數data 以及 第5步產生的nonce進行加密,存入encrypted_data

8. 填充結構體

aIVBytes使用第6步創建的隨機數填充,aHashBytes使用第4步的hash填充,aCipherBytes使用第7步計算的encrypted_data填充。而其他節點的值則按照結構體註釋中的填寫,如果不想使用默認值進行相應調整

9. 對結構體進行BASE64編碼

以下是用C++代碼實現的整個計算過程,我們使用openssl提供的SHA_HMAC,TripleDes algorithm以及BASE64方法。

  1. char* TCreateSSOP::GetSSOP(void)
  2. {
  3. MSGUSRKEY key;
  4. _NS_compute_usrkey(&key, (char*)m_szNonce.c_str(), (char*)m_szBinarySecret.c_str());
  5. return base64((unsignedchar*)&key,sizeof(MSGUSRKEY));
  6. }
  7. int TCreateSSOP::_NS_compute_usrkey(MSGUSRKEY *key,char *challenge,char *secret)
  8. {
  9. char *key1;
  10. char key2[24];
  11. char key3[24];
  12. int klen = 0;
  13. char *chg;
  14. DES_cblock iv = {0,0,0,0,0,0,0,0};
  15. DES_key_schedule sched1, sched2, sched3;
  16. const_DES_cblock dkey1, dkey2, dkey3;
  17. key->uStructHeaderSize = 28;
  18. key->uCryptMode = 1;
  19. key->uCipherType = 0x6603;
  20. key->uHashType = 0x8004;
  21. key->uIVLen = 8;
  22. key->uHashLen = 20;
  23. key->uCipherLen = 72;
  24. memset(key->aIVBytes, 0, 8);
  25. key1 = (char*)unbase64((unsignedchar*)secret, strlen(secret));
  26. klen = 24;
  27. _NS_compute_hash(key1, klen, "WS-SecureConversationSESSION KEY HASH", key2);
  28. _NS_compute_hash(key1, klen, "WS-SecureConversationSESSION KEY ENCRYPTION", key3);
  29. HMAC(EVP_sha1(), (unsigned char*)key2, klen, (unsignedchar*)challenge, strlen(challenge), key->aHashBytes, NULL);
  30. memcpy(dkey1, key3, 8);
  31. memcpy(dkey2, key3+8, 8);
  32. memcpy(dkey3, key3+16, 8);
  33. DES_set_key_unchecked(&dkey1, &sched1); // set the key schedule
  34. DES_set_key_unchecked(&dkey2, &sched2); // set the key schedule
  35. DES_set_key_unchecked(&dkey3, &sched3); // set the key schedule
  36. chg = (char*)malloc(strlen(challenge)+9);
  37. memcpy(chg, challenge, strlen(challenge));
  38. memset(chg+strlen(challenge), 8, 8);
  39. *(chg+strlen(challenge)+8) = 0;
  40. DES_ede3_cbc_encrypt((unsigned char*)chg, key->aCipherBytes, 72, &sched1, &sched2, &sched3, &iv, DES_ENCRYPT);
  41. free(chg);
  42. free(key1);
  43. return 0;
  44. }
  45. char* TCreateSSOP::_NS_compute_hash(char *key,int klen,constchar *magic,char *result)
  46. {
  47. int mlen = strlen(magic);
  48. staticchar ret[24];
  49. unsigned int mdlen1, mdlen2, mdlen3, mdlen4;
  50. unsigned char hash1[EVP_MAX_MD_SIZE];
  51. unsigned char hash2[EVP_MAX_MD_SIZE];
  52. unsigned char hash3[EVP_MAX_MD_SIZE];
  53. unsigned char hash4[EVP_MAX_MD_SIZE];
  54. unsigned char buf[EVP_MAX_MD_SIZE*2];
  55. HMAC(EVP_sha1(), (unsigned char*)key, klen, (unsignedchar*)magic, mlen, hash1, &mdlen1);
  56. memcpy(buf, hash1, mdlen1);
  57. memcpy(buf+mdlen1, magic, mlen);
  58. HMAC(EVP_sha1(), (unsigned char*)key, klen, buf, mlen+mdlen1, hash2, &mdlen2);
  59. HMAC(EVP_sha1(), (unsigned char*)key, klen, hash1, mdlen1, hash3, &mdlen3);
  60. memcpy(buf, hash3, mdlen3);
  61. memcpy(buf+mdlen3, magic, mlen);
  62. HMAC(EVP_sha1(), (unsigned char*)key, klen, buf, mdlen3+mlen, hash4, &mdlen4);
  63. memcpy(ret, hash2, 20);
  64. memcpy(ret+20, hash4, 4);
  65. memcpy(result,ret, 24);
  66. return ret;
  67. }
  68. char* base64(const unsignedchar *input,int length)
  69. {
  70. //如果沒制定長度,則根據要加密字符串的長度
  71. if (length == 0)
  72. {
  73. length = strlen((constchar*)input);// 需要加密長度
  74. }
  75. //計算存放加密數據的長度
  76. int bufferLen = length/3*4 + (length%3 ?4 : 0) + 1;
  77. char *buffer = (char*)malloc(bufferLen);
  78. memset(buffer, 0, bufferLen);
  79. //base64加密
  80. EVP_EncodeBlock((unsigned char*)buffer, input, length);
  81. return buffer;
  82. }

.

.

三 小結

自此,我們成功的完成了從連接DS服務器到MSN的登錄身份驗證,注意,這裏僅僅是完成身份驗證,並不是完成了實際的登錄過程。除此之外,我們還獲得了和麪獲取聯繫人列表、離線消息,聯繫人頭像等操作時,請求Webservice服務所需要的ticket。整個登錄過程複雜的就在於構建SSO請求的XML,以及計算p值。

在登錄過程中可能出現的錯誤就是賬號或密碼錯誤導致SSO認證失敗,也可能是計算P值出錯,導致登錄驗證失敗。所以在程序中必須對這兩種錯誤進行處理。

轉自:http://blog.csdn.net/cc_net/article/details/5986215

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章