您的位置:首页 > 其它

rtmp complex handshake,支持h264/aac

2016-09-30 15:52 323 查看
本文转自win_lin,链接地址:http://blog.csdn.net/win_lin/article/details/13006803
,然后添加了一些自己的注释

当服务器和客户端的握手是按照rtmp协议进行,是不支持h264/aac的,有数据,就是没有视频和声音。

原因是adobe变更了握手的数据结构,标准rtmp协议的握手的包是随机的1536字节(S1S2C1C2),变更后的是需要进行摘要和加密。

rtmp协议定义的为simple handshake,变更后加密握手可称为complex handshake。

本文详细分析了rtmpd(ccrtmpserver)中的处理逻辑,以及rtmpdump的处理逻辑,从一个全是魔法数字的世界找到他们的数据结构和算法。


complex handshake C1S1结构

complex handshake将C1S1分为4个部分,它们的顺序(schema)一种可能是:

[plain] view
plain copy

 





// c1s1 schema0  

time: 4bytes  

version: 4bytes  

key: 764bytes  

digest: 764bytes  

其中,key和digest可能会交换位置,即schema可能是:

[plain] view
plain copy

 





// c1s1 schema1  

time: 4bytes  

version: 4bytes  

digest: 764bytes  

key: 764bytes  

客户端来决定使用哪种schema,服务器端则需要先尝试按照schema0解析,失败则用schema1解析。如下图所示:



无论key和digest位置如何,它们的结构是不变的:

[plain] view
plain copy

 





// 764bytes key结构  

random-data: (offset)bytes  

key-data: 128bytes  

random-data: (764-offset-128-4)bytes  

offset: 4bytes  

  

// 764bytes digest结构  

offset: 4bytes  

random-data: (offset)bytes  

digest-data: 32bytes  

random-data: (764-4-offset-32)bytes  

offset 的计算方法比较特殊,并不是简单的大端序小端序,而是这四个字节累加,然后模上728 = 764-4-32

for(i=0;i<4;i++)
{
offset += CBitsOP::Get8Bits(start,end,TRUE);
}
offset = offset % 728;


备注:发现FMS只认识digest-key结构。

如下图所示:





crtmp中这些全是魔法数字。


complex handshake C2S2结构

C2S2主要是提供对C1S1的验证。结构如下:

[plain] view
plain copy

 





// 1536bytes C2S2结构  

random-data: 1504bytes  

digest-data: 32bytes  

C2S2的结构相对比较简单。如下图所示:



下面介绍C1S1C2S2的生成以及验证算法。


complex handshake C1S1算法

C1S1中的digest都是包含32字节的digest-data,而且digest-data将C1S1分成两部分:

[plain] view
plain copy

 





// C1S1被digest分成两部分  

c1s1-part1: n bytes  

digest-data: 32bytes  

c1s1-part2: (1536-n-32)bytes  

如下图所示:



在生成C1时,需要用到c1s1-part1和c1s1-part2这两个部分的字节拼接起来的字节,定义为:

[plain] view
plain copy

 





c1s1-joined = bytes_join(c1s1-part1, c1s1-part2)  

也就是说,把1536字节的c1s1中的32字节的digest拿剪刀剪掉,剩下的头和尾加在一起就是c1s1-joined。

用到的两个常量FPKey和FMSKey:

[plain] view
plain copy

 





u_int8_t FMSKey[] = {  

    0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20,  

    0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c,  

    0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69,  

    0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,  

    0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001  

    0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8,  

    0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57,  

    0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab,  

    0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae  

}; // 68  

  

u_int8_t FPKey[] = {  

    0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20,  

    0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C,  

    0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79,  

    0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001  

    0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8,  

    0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57,  

    0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,  

    0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE  

}; // 62  

生成C1的算法如下:

[plain] view
plain copy

 





calc_c1_digest(c1, schema) {  

    get c1s1-joined from c1 by specified schema  

    digest-data = HMACsha256(c1s1-joined, FPKey, 30)  

    return digest-data;  

}  

random fill 1536bytes c1 // also fill the c1-128bytes-key  

time = time() // c1[0-3]  

version = [0x80, 0x00, 0x07, 0x02] // c1[4-7]  

schema = choose schema0 or schema1  

digest-data = calc_c1_digest(c1, schema)  

copy digest-data to c1  

生成S1的算法如下:

[plain] view
plain copy

 





/*decode c1 try schema0 then schema1*/  

c1-digest-data = get-c1-digest-data(schema0)  

if c1-digest-data equals to calc_c1_digest(c1, schema0) {  

    c1-key-data = get-c1-key-data(schema0)  

    schema = schema0  

} else {  

    c1-digest-data = get-c1-digest-data(schema1)  

    if c1-digest-data not equals to calc_c1_digest(c1, schema1) {  

        switch to simple handshake.  

        return  

    }  

    c1-key-data = get-c1-key-data(schema1)  

    schema = schema1  

}  

  

/*generate s1*/  

random fill 1536bytes s1  

time = time() // c1[0-3]  

version = [0x04, 0x05, 0x00, 0x01] // s1[4-7]  

s1-key-data=shared_key=DH_compute_key(peer_pub_key=c1-key-data)  

get c1s1-joined by specified schema  

s1-digest-data = HMACsha256(c1s1-joined, FMSKey, 36)  

copy s1-digest-data and s1-key-data to s1.  

C1S1的算法完毕。


complex handshake C2S2

C2S2的生成算法如下:

[plain] view
plain copy

 





random fill c2s2 1536 bytes  

  

// client generate C2, or server valid C2  

temp-key = HMACsha256(s1-digest, FPKey, 62)  

c2-digest-data = HMACsha256(c2-random-data, temp-key, 32)  

  

// server generate S2, or client valid S2  

temp-key = HMACsha256(c1-digest, FMSKey, 68)  

s2-digest-data = HMACsha256(s2-random-data, temp-key, 32)  

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  rtmp complex handsha
相关文章推荐