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分为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;
如下图所示:
crtmp中这些全是魔法数字。
C2S2主要是提供对C1S1的验证。结构如下:
[plain] view
plain copy
// 1536bytes C2S2结构
random-data: 1504bytes
digest-data: 32bytes
C2S2的结构相对比较简单。如下图所示:
下面介绍C1S1C2S2的生成以及验证算法。
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的算法完毕。
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协议进行,是不支持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 handshake,变更的握手,支持h264/aac
- rtmp complex handshake code
- 公布一个软件,轻新视频录播程序,H264/AAC录制视音频,保存FLV,支持RTMP直播
- rtmp simple handshake
- 发布一个软件,轻新视频录播程序,H264/AAC录制视音频,保存FLV,支持RTMP直播
- EasyRTMP+EasyRTSPClient实现的多路(支持断线重连)RTSP转RTMP直播推流工具
- centos svn checkout 错误 SSL handshake failed: SSL error: Key usage violation in certificate has bee
- Re-negotiation handshake failed: Not accepted b...
- linux上svn连接visual svn server时ssl鉴权失败,问题解决 SSL handshake failed: SSL error: Key usage violation in ce
- Debugger failed to attach: handshake failed - connection prematurally closed 解决方法
- TCP 3次握手模式(3-WAY HANDSHAKE)
- nginx SSL handshake fails on requests from mobile devices with “SSL_BYTES_TO_CIP
- nginx搭建支持http和rtmp协议的流媒体服务器之三
- SSB – Connection handshake failed
- iOS之一个超赞的视频直播、第三方库,直播看这个就够了,支持RTMP推流,美颜直播
- TLS Handshake Flow(extracts from RFCs)
- TCP三次握手(Three-way Handshake)
- websockets handshake using netcat/bash script
- PLDroidPlayer 是 Pili 直播 SDK 的安卓播放器。支持所有直播常用的格式,如:RTMP、HLS、FLV。拥有优秀的功能和特性,如:首屏秒开、追帧优化、丰富的数据和状态回调、硬解软解
- Security:090476]Invalid/unknown SSL header was received from peer locahost during SSL handshake