RTMP握手简介
rtmp 1.0规范中,指定了RTMP的握手协议:
- c0/s0:一个字节,说明是明文还是加密。
- c1/s1: 1536字节,4字节时间,4字节0x00,1528字节随机数
- c2/s2: 1536字节,4字节时间1,4字节时间2,1528随机数和s1相同。
这个就是srs以及其他开源软件的simple handshake,简单握手,标准握手,FMLE也是使用这个握手协议。
Flash播放器连接服务器时,如果服务器只支持简单握手,则无法播放h264和aac的流,有数据,但没有视频和声音。
- 原因是adobe变更了握手的数据结构,标准rtmp协议的握手的包是随机的1536字节(S1S2C1C2),变更后的是需要进行摘要和加密。
- adobe将简单握手改为了有一系列加密算法的复杂握手(complex handshake)
simple简单握手和complex复杂握手的主要区别:
SRS编译时若打开了SSL选项(–with-ssl),SRS会先使用复杂握手和客户端握手,若复杂握手失败,则尝试简单握手。
complex handshake C1S1结构
1. complex handshake将C1,S1分为4个部分,它们的顺序(schema)可能是:
// c1s1 schema0
time: 4bytes
version: 4bytes
key: 764bytes
digest: 764bytes
其中key和digest可能会交换位置,即schema可能是:
// c1s1 schema1
time: 4bytes
version: 4bytes
digest: 764bytes
key: 764bytes
2. 客户端来决定使用哪种schema,服务器端则需要先尝试按照schema0解析,失败则用schema1解析。如下图所示:
3. 无论key和digest位置如何,它们的结构是不变的:
// 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
备注:发现FMS只认识digest-key结构。
对应代码
代码在SrsComplexHandshake::handshake_with_client下:
hs_bytes->read_c0c1(io)
srs_error_t SrsHandshakeBytes::read_c0c1(ISrsProtocolReader* io)
{
srs_error_t err = srs_success;
if (c0c1) {
return err;
}
ssize_t nsize;
c0c1 = new char[1537];
if ((err = io->read_fully(c0c1, 1537, &nsize)) != srs_success) {
return srs_error_wrap(err, "read c0c1");
}
// Whether RTMP proxy, @see https://github.com/ossrs/go-oryx/wiki/RtmpProxy
if (uint8_t(c0c1[0]) == 0xF3) { //没进
uint16_t nn = uint16_t(c0c1[1])<<8 | uint16_t(c0c1[2]);
ssize_t nn_consumed = 3 + nn;
if (nn > 1024) {
return srs_error_new(ERROR_RTMP_PROXY_EXCEED, "proxy exceed max size, nn=%d", nn);
}
// 4B client real IP.
if (nn >= 4) {
proxy_real_ip = uint32_t(c0c1[3])<<24 | uint32_t(c0c1[4])<<16 | uint32_t(c0c1[5])<<8 | uint32_t(c0c1[6]);
nn -= 4;
}
memmove(c0c1, c0c1 + nn_consumed, 1537 - nn_consumed);
if ((err = io->read_fully(c0c1 + 1537 - nn_consumed, nn_consumed, &nsize)) != srs_success) {
return srs_error_wrap(err, "read c0c1");
}
}
return err;
}
c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema0)
srs_error_t c1s1::parse(char* _c1s1, int size, srs_schema_type schema)
{
srs_assert(size == 1536);
if (schema != srs_schema0 && schema != srs_schema1) {
return srs_error_new(ERROR_RTMP_CH_SCHEMA, "parse c1 failed. invalid schema=%d", schema);
}
SrsBuffer stream(_c1s1, size);
time = stream.read_4bytes(); //4B time
version = stream.read_4bytes(); // client c1 version 4B version
srs_freep(payload);
if (schema == srs_schema0) { //判断顺序
payload = new c1s1_strategy_schema0();
} else {
payload = new c1s1_strategy_schema1();
}
return payload->parse(_c1s1, size); //解析payload,payload由key和digest组成
}
payload->parse(_c1s1, size) //解析payload,payload由key和digest组成
srs_error_t c1s1_strategy_schema0::parse(char* _c1s1, int size)
{
srs_error_t err = srs_success;
srs_assert(size == 1536);
if (true) {
SrsBuffer stream(_c1s1 + 8, 764);
if ((err = key.parse(&stream)) != srs_success) { //解析key
return srs_error_wrap(err, "parse the c1 key");
}
}
if (true) {
SrsBuffer stream(_c1s1 + 8 + 764, 764);
if ((err = digest.parse(&stream)) != srs_success) { //解析digest
return srs_error_wrap(err, "parse the c1 digest");
}
}
return err;
}
complex handshake C2S2结构
C2S2主要是提供对C1S1的验证。结构如下:
// 1536bytes C2S2结构
random-data: 1504bytes
digest-data: 32bytes
C2S2的结构相对比较简单。如下图所示:
对应代码:
s2.s2_create(&c1)
srs_error_t c2s2::s2_create(c1s1* c1)
{
srs_error_t err = srs_success;
char temp_key[SRS_OpensslHashSize];
if ((err = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) != srs_success) {
return srs_error_wrap(err, "create s2 temp key");
}
char _digest[SRS_OpensslHashSize];
if ((err = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != srs_success) {
return srs_error_wrap(err, "create s2 digest");
}
memcpy(digest, _digest, 32);
return err;
}
s2.s2_validate(&c1, is_valid)
srs_error_t c2s2::s2_validate(c1s1* c1, bool& is_valid)
{
is_valid = false;
srs_error_t err = srs_success;
char temp_key[SRS_OpensslHashSize];
if ((err = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) != srs_success) {
return srs_error_wrap(err, "create s2 temp key");
}
char _digest[SRS_OpensslHashSize];
if ((err = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != srs_success) {
return srs_error_wrap(err, "create s2 digest");
}
is_valid = srs_bytes_equals(digest, _digest, 32);
return err;
}
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。