1. 程式人生 > >rtmfp協議中的加解密原理

rtmfp協議中的加解密原理

1.加解密原理

1.1.原理      在DH密碼系統中,p和g是公開的,發起者和接收者兩端的p和g必須相同。特殊地,g等於2,p是一個1024位長的數字。發起者生成一個隨機的1024位長的私有數字(x1),以此建立1024位長的DH公開數(y1)。       y1 = g ^ x1 % p      當目標接收到這條訊息後。它會生成一個新的1024位隨機數作為DH私有數(x2),和1024位的DH公開數(y2)。       y2 = g ^ x2 % p      發起者和反饋者要相互交換y1和y2,然後兩端分別計算shared-secret,兩端算出的結果應該是一樣的。       shared-secret = y1 ^ x2 % p = y2 ^ x1 % p       除了shared-secret,發起者和反饋者兩端還需要交換各自的initiator-nonce和responder-nonce,然後就可以計算出各自的加解密祕鑰。       decode key = HMAC-SHA256(shared-secret, HMAC-SHA256(responder nonce, initiator nonce))       encode key = HMAC-SHA256(shared-secret, HMAC-SHA256(initiator nonce, responder nonce))       cumulus使用的公開數p。 unsigned char p[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 1.2.握手階段加解密
     handshake階段,使用預設的sessionid=0,預設的加解密祕鑰“Adobe Systems 02”。四部握手的加解密都要使用這個祕鑰,但是第四部的時候要使用iikeying協議中獲取到的sessionid。 1.3.祕鑰交換      祕鑰交換指的是交換公開數y1,y2,initiator-nonce和responder-nonce。客戶端與伺服器端祕鑰交換,客戶端之間祕鑰交換有些區別。certificate和nonce中存放的資料結構都是OPTION。      客戶端與伺服器端祕鑰交換時,y1存放在IIKeying的certificate中,後128位元組,y2存放在RIIKeying的nonce中,後128位元組。      客戶端之間祕鑰交換時,y1存放在IIKeying的certificate中,後128位元組,y2存放在RIHello的certificate中,後128位元組。

2.與伺服器互動 2.1.四步握手 2.1.1.IHello      略。
2.1.2.RIHello      certificate無用。
2.1.3.IIKeying      certificate中只有一個option,結構為0x81,0x02,0x1d,0x02 + 128B隨機數(y1)。      nonce中包含多個option,結構為0x02,0x1d,0x02,0x41,0x0e + 64B隨機數 + 0x03,0x1a,0x02,x0a,0x02,0x1e,0x02。 2.1.4.RIIKeying      nonce中包含多個option,結構為0x03,0x1a,0x00,0x00,0x02,0x1e,0x00,0x41,0x0e + 128B隨機數(y2)+ 0x03,0x1a,0x00,0x00,0x02,0x1e,x00。 2.2.建立會話

2.2.1.connect命令 傳送包結構 0x80 + flowid + seqnum + fsn + option(0x050,x00,0x54,0x43,0x04,x00) + 5B(0x14,0x00,0x00,0x00,0x00) + amf Name:                  no-name., STRING:   connect
Name:                  no-name., NUMBER:   1.00
Name:                       app, STRING:
Name:                  flashVer, STRING:   WIN 11,1,102,55
Name:                    swfUrl, UNDEFINED
Name:                     tcUrl, STRING:   rtmfp://192.168.200.39
Name:                      fpad, BOOLEAN:  FALSE
Name:              capabilities, NUMBER:   235.00
Name:               audioCodecs, NUMBER:   3575.00
Name:               videoCodecs, NUMBER:   252.00
Name:             videoFunction, NUMBER:   1.00
Name:                   pageUrl, UNDEFINED
Name:            objectEncoding, NUMBER:   3.00 返回包結構 0x80 + flowid + seqnum + fsn + option(0x050,x00,0x54,0x43,0x04,x00;0x02,0x0a,0x02) + 5B(0x14,0x00,0x00,0x00,0x00) + amf Name:                  no-name., STRING:   _result
Name:                  no-name., NUMBER:   1.00
Name:                  no-name., NULL
Name:                     level, STRING:   status
Name:                      code, STRING:   NetConnection.Connect.Success
Name:               description, STRING:   Connection succeeded
Name:            objectEncoding, NUMBER:   3.00 2.2.2.setpeerinfo命令 傳送包結構 0x00 + flowid + seqnum + fsn + 5B(0x11,0x00,0x00,0x00,0x00,0x00) + amf Name:           no-name., STRING:   setPeerInfo Name:           no-name., NUMBER:   0.00 Name:           no-name., STRING:   192.168.202.91:1935 返回包結構 0x00 + flowid + seqnum + fsn + 7B(0x04,0x00,0x00,0x00,0x00,0x00,0x29) + 4B(server keepalive) + 4B(peer keepalive) 2.3.如果加入group      略 完整流程


3.客戶端間互動 3.1四部握手 3.1.1.IHello      略。
3.1.2.FIHello       fihello命令中只有一個initiator地址,但是server可能會觀察到initiator多個地址,如redirect中flag=1的地址。如果responder不迴應initiator,則server回定時重發fihello命令,其中包含的地址會以輪詢的方式變化,第一個fihello中的地址為flag=2的地址。
3.1.3.Redirect       redirect命令中包含多個地址,每個地址前面有個flag,flag=2的地址是客戶端自己上報的(準確地址),flag=1的地址為伺服器觀察到的(可能不準確),我們最好使用flag=2的地址。
flag 2, ip 192.168.202.91, port 58716 flag 1, ip 192.168.66.1, port 58716 flag 1, ip 192.168.108.1, port 58716 flag 1, ip 192.168.202.91, port 58716
3.1.4.RHello       certificate中只有一個option,結構為0x81,0x02,0x1d,0x02 + 128B隨機數(y2)。 3.1.5.IIKeying       certificate中只有一個option,結構為0x81,0x02,0x1d,0x02 + 128B隨機數(y1)。       nonce中包含多個option,結構為0x02,0x1d,0x02,0x41,0x0e + 64B隨機數 + 0x03,0x1a,0x02,x0a,0x02,0x1e,0x02。 3.1.6.RIIKeying       nonce中包含多個option,結構為0x03,0x1a,0x00,0x00,0x02,0x1e,0x00,0x41,0x0e + 64B隨機數。 3.2會話建立過程      兩個peer之間需要建立兩個連線(4個數據流),分別為initiator到responder的連線和responder到initiator的連線,連線的建立過程是相同的,都是由play命令開始的。注意這裡的連線值的是NetStream連線,而不是NetConnection連線。NetConnection的連線在整個點播過程中只有1個,而NetStream的連線每兩個客戶端之間就有2個。一次連線的互動過程如下所示。

play資料包 0x80 + flowid + seqnum + fsn + option(0x050,x00,0x54,0x43,0x04,x05) + 6B(0x11,4B隨機,0x00) + amf Name:                  no-name., STRING:        play
Name:                  no-name., NUMBER:        0.00
Name:                  no-name., STRING:        data-stream(資料流名字,應用程式覺定) play確認包       play後面會跟隨兩個一樣的確認包(除了時間戳略有差異),其實一個確認包就夠了。      下面的四個chunk有可能是一次性收到的,第一個chunk是0x10,後面三個是0x11,也有可能是分多次收到的。 |RtmpSampleAccess包(這個包可以忽略) 0x80 + flowid + seqnum + fsn + option(0x050,x00,0x54,0x43,0x04,x02;0x02,0x0a,0x02) + 6B(0x0f,0x00,0x00,0x00,0x00,0x00) + amf Name:                  no-name., STRING:   |RtmpSampleAccess Name:                  no-name., BOOLEAN:  FALSE Name:                  no-name., BOOLEAN:  FALSE 11B包(這個包可以忽略) 0x00 +  11B 觀察到的值:0x04,0xc6,0x0c,0xbe,0x16,0x00,0x00,0x00,0x00,0x00,0x02 觀察到的值:0x04,0xc6,0x17,0xb6,0x7c,0x00,0x00,0x00,0x00,0x00,0x02 NetStream.Play.Reset包 0x00 + 6B(0x11,0x00,0x00,0x00,0x00,0x00) + amf Name:                  no-name., STRING:   onStatus Name:                  no-name., NUMBER:   0.00 Name:                  level, STRING:   status Name:                  code, STRING:   NetStream.Play.Reset Name:                  description, STRING:   Playing and resetting data-stream NetStream.Play.Start包 0x00 + 6B(0x11,0x00,0x00,0x00,0x00,0x00) + amf Name:                  no-name., STRING:   onStatus Name:                  no-name., NUMBER:   0.00 Name:                  level, STRING:   status Name:                  code, STRING:   NetStream.Play.Start Name:                  description, STRING:   Started playing data-stream NetStream確認包      兩個一樣的確認包。 整個互動流程可以簡化為

3.3.流關聯方法      每一個連線建立的過程中需要建立2個數據流。play命令中會帶一個flow1,後面跟著的確認包表示flow1建立完成。NetStream命令中會帶一個flow2,後面跟著的確認包表示flow2建立完成。我們還需要讓flow1和flow2相互關聯,方法是在NetStream的OPTION中設定。OPTION有兩種,如下所示。      第一個OPTION表示資料流的元資訊,具體含義不明。例如:0x050,x00,0x54,0x43,0x04,x05。

     第二個OPTION表示當前資料包使用的流與flowID相關聯,三個欄位都是VLU格式。

     在play命令中會帶第一種OPTION,NetStream命令中兩種OPTION都帶,第一個OPTION直接使用play中的OPTION,第二個OPTION中的flowID填寫play帶過來的flowID,表示兩個flow相互關聯。

3.4.資料傳送      當兩邊的雙向資料流建立完畢後,就可以正式傳送資料了,接收端收到的資料與傳送端有關係。      傳送端呼叫: sendStream.send("__data", args),第一個引數是要求接收端實現的回撥函式名,接收端必須實現這個函式來處理後面跟著的資料。傳送端傳送的內容就是args字串。      接收端收到的資料如下: 0x00 + flowid + seqnum + fsn + 6B(0x0f,0x00,0x00,0x9b,0x70,0x00) + amf + 6B(0x11,0x09,0x03,0x01,0x06,0x07) + 內容 4.部分細節描述 4.1.訊息重發      所有用0x10訊息擴充套件的協議都必須由0x51確認,否則就必須走重發流程。      舉例:4步握手之後,client會發送由0x10擴充套件的connect命令,如果server不返回0x51確認,則client會一直重發connect,大約傳送了15次,才宣告連線失敗。如果server這時候返回了0x51則client就不會重發connect命令了,這時候client會等待server傳送connectresult命令,一直等下去。 4.2.handshake過程中重發包      在handshake過程中,initiator給responder傳送0x30,0x38,如果responder沒有返回包,則會重發,重發間隔類似一個等差數列。responder向initiator傳送0x70,如果這時候沒有收到後續包不需要重發,重發都是由initiator觸發的。 4.3.rtmfp域名訪問方式      用rtmfp協議訪問域名時,flash會向該域名下所有伺服器傳送ihello協議,伺服器返回rhello協議,flash先收到哪個rhello協議就使用那一臺伺服器。 4.4.redirect與fihello協議      redirect協議會返回多個打孔被動方地址,fihello只返回打孔主動方一個地址。redirect協議的sessionid=0,fihello的sessionid為客戶端與伺服器建立的id。