5.4. handshake — Server Certificate
当服务器确定了CipherSuite后,依据CipherSuite外面的认证算法,如果须要发送证书给客户端,那么就发送 Server Certificate音讯给客户端。Server Certificate总是在ServerHello之后立刻发送,所以在同一个RTT里。
Server Certificate外面蕴含了服务器的证书链。
音讯构造:
opaque ASN.1Cert<1..2^24-1>;struct { ASN.1Cert certificate_list<0..2^24-1>;} Certificate;
certificate_list
: 证书列表,发送者的证书必须是第一个,后续的每一个证书都必须是前一个的签订证书。根证书能够省略
证书申请的时候,个别会收到好几个证书,有的须要本人依照这个格局来拼接成证书链。
如果服务器要认证客户端的身份,那么服务器会发送Certificate Request音讯,客户端应该也以 这条Server Certificate音讯的格局回复。
服务器发送的证书必须:
- 证书类型必须是 X.509v3。除非明确地协商成别的了(比拟少见,rfc里提到了例如 [OpenPGP格局] 链接 https://tools.ietf.org/html/r... )。
- 服务器证书的公钥,必须和抉择的密钥替换算法配套。
密钥替换+认证算法 | 配套的证书中公钥类型 |
---|---|
RSA / RSA_PSK | RSA 公钥;证书中必须容许私钥用于加密 (即如果应用了X509V3规定的key usage扩大, keyEncipherment比特位必须置位) 这种用法没有前向安全性,因而在 TLS 1.3中被废除了 |
DHE_RSA / ECDHE_RSA | RSA 公钥;证书中必须容许私钥用于签名(即如果应用了X509V3规定的key usage扩大, digitalSignature比特位必须置位),并且容许server key exchange音讯将要应用的签名模式(例如 PKCS1_V1.5 ,OAEP等)和hash算法(例如sha1, sha256等) |
DHE_DSS | DSA 公钥; 历史遗留产物,素来没有被大规模用过,安全性差,废除状态。证书必须容许私钥用于签名,必须容许server key exchange音讯中应用的hash算法。 |
DH_DSS / DH_RSA | Diffie-Hellman 公钥; 要求key usage外面的keyAgreement比特位必须置位。 这种用法没有前向安全性,因而在 TLS 1.3中被废除了 |
ECDH_ECDSA / ECDH_RSA | 能做 ECDH 用处的公钥;公钥必须应用 客户端反对的ec曲线和点格局。这种用法没有前向安全性,因而在 TLS 1.3中被废除了 |
ECDHE_ECDSA | ECDSA用处的公钥;证书必须运输私钥用作签名,必须容许server key exchange音讯外面要用到的hash算法。公钥必须应用客户端反对的ec曲线和点格局。 |
- “server_name” 和 “trusted_ca_keys” 扩大用于疏导证书抉择。
其中有5种是ECC密钥替换算法:ECDH_ECDSA, ECDHE_ECDSA, ECDH_RSA, ECDHE_RSA, ECDH_anon。
ECC(椭圆曲线)体制相比RSA,因为公钥更小,性能更高,所以在挪动互联网环境下越发重要。
以上ECC的5种算法都用ECDH来计算premaster secret, 仅仅是ECDH密钥的生命周期和认证算法不同。
其中只有 ECDHE_ECDSA 和 ECDHE_RSA 是前向平安的。
如果客户端在ClientHello里提供了 “signature_algorithms” 扩大,那么服务器提供的所有证书必须用 “signature_algoritms”中提供的 hash/signature算法对 之一签订。要留神的是,这意味着,一个蕴含某种签名算法密钥的证书,可能被另一种签名算法签订(例如,一个RSA公钥可能被一个ECDSA公钥签订)。(这在TLS1.2和TLS1.1中是不一样的,TLS1.1要求所有的算法都雷同。)留神这也意味着DH_DSS,DH_RSA,ECDH_ECDSA,和ECDH_RSA 密钥替换不限度签订证书的算法。固定DH证书可能应用”signature_algorithms”扩大列表中的 hash/签名算法对 中的某一个签订。名字 DH_DSS, DH_RSA, ECDH_ECDSA, 和 ECDH_RSA 只是历史起因,这几个名字的后半局部中指定的算法,并不会被应用,即DH_DSS中的DSS并不会被应用,DH_RSA中并不会应用RSA做签名,ECDH_ECDSA并不会应用ECDSA算法。。。
如果服务器有多个证书,就必须从中抉择一个,个别依据服务器的外网ip地址,SNI中指定的hostname,服务器配置来做抉择。如果服务器只有一个证书,那么要确保这一个证书合乎这些条件。
要留神的是,存在一些证书应用了TLS目前不反对的 算法组合。例如,应用 RSASSA-PSS签名公钥的证书(即证书的SubjectPublicKeyInfo字段是id-RSASSA-PSS)。因为TLS没有给这些算法定义对应的签名算法,这些证书不能在TLS中应用。
如果一个CipherSuite指定了新的TLS密钥替换算法,也会指定证书格局和要求的密钥编码方法。
5.5. handshake — Server Key Exchange
服务器会在 server Certificate 音讯之后,立刻发送 Server Key Exchange音讯。
(如果协商出的CipherSuite不须要做认证,即anonymous negotiation,会在ServerHello之后立刻发送Server Key Exchange音讯)
只有在server Certificate 音讯没有足够的信息,不能让客户端实现premaster的密钥替换时,服务器才发送 server Key Exchange, 次要是对前向平安的几种密钥协商算法,列表如下:
- DHE_DSS
- DHE_RSA
- DH_anon
- ECDHE_ECDSA
- ECDHE_RSA
- ECDH_anon
对上面几种密钥替换办法,发送ServerKeyExchange音讯是非法的:
- RSA
- DH_DSS
- DH_RSA
- ECDH_ECDSA
- ECDH_RSA
须要留神的是,ECDH和ECDSA公钥的数据结构是一样的。所以,CA在签订一个证书的时候,可能要应用 X.509 v3 的 keyUsage 和 extendedKeyUsage 扩大来限定ECC公钥的应用形式。
ServerKeyExchange传递足够的信息给客户端,来让客户端替换premaster secret。个别要传递的是:一个 Diffie-Hellman 公钥,或者一个其余算法(例如RSA)的公钥。
在TLS理论部署中,咱们个别只应用这4种:ECDHE_RSA, DHE_RSA, ECDHE_ECDSA,RSA
其中RSA密钥协商(也能够叫密钥传输)算法,因为没有前向安全性,在TLS 1.3外面曾经被破除了。参见: [Confirming Consensus on removing RSA key Transport from TLS 1.3 ] 链接 http://www.ietf.org/mail-arch...
音讯格局:
enum { dhe_dss, dhe_rsa, dh_anon, rsa, dh_dss, dh_rsa, ec_diffie_hellman } KeyExchangeAlgorithm;struct { opaque dh_p<1..2^16-1>; opaque dh_g<1..2^16-1>; opaque dh_Ys<1..2^16-1>;} ServerDHParams; /* Ephemeral DH parameters */dh_p Diffie-Hellman密钥协商计算的大质数模数。dh_g Diffie-Hellman 的生成元,dh_Ys 服务器的Diffie-Hellman公钥 (g^X mod p). struct { opaque point <1..2^8-1>; } ECPoint; enum { explicit_prime (1), explicit_char2 (2), named_curve (3), reserved(248..255) } ECCurveType; struct { ECCurveType curve_type; select (curve_type) { case named_curve: NamedCurve namedcurve; }; } ECParameters; struct { ECParameters curve_params; ECPoint public; //ECDH的公钥 } ServerECDHParams;struct { select (KeyExchangeAlgorithm) { case dh_anon: ServerDHParams params; case dhe_dss: case dhe_rsa: ServerDHParams params; digitally-signed struct { opaque client_random[32]; opaque server_random[32]; ServerDHParams params; } signed_params; case ec_diffie_hellman: ServerECDHParams params; Signature signed_params; case rsa: case dh_dss: case dh_rsa: struct {} ; /* message is omitted for rsa, dh_dss, and dh_rsa */ /* may be extended, e.g., for ECDH -- see [TLSECC] */ };} ServerKeyExchange;params 服务器的密钥替换参数。signed_params 对须要认证的(即非anonymous的)密钥替换,对服务器的密钥替换参数的数字签名。
ECParameters 构造比拟麻烦,其中ECCurveType是反对3种曲线类型的,能够自行指定椭圆曲线的多项式系数,基点等参数。然而,咱们根本不会用到这种性能,因为个别部署都是应用 NamedCurve,即参数曾经事后选定,各种密码学库广泛都反对的一组曲线,其中目前用的最广的是 secp256r1 (还被称为 P256,或 prime256v1)
NamedCurve 列表中比拟重要的曲线(在TLS1.3中,只保留了这几条曲线。),定义如下:
enum { ... secp256r1 (23), secp384r1 (24), secp521r1 (25), reserved (0xFE00..0xFEFF), (0xFFFF)} NamedCurve;
ECDHE_RSA 密钥替换算法的 SignatureAlgorithm 是 rsa 。
ECDHE_RSA 密钥替换算法的 SignatureAlgorithm 是 ecdsa。
如果客户端提供了 “signature_algorithms” 扩大, 则签名算法和hash算法必须是列在扩大中的算法。
要留神的是,这个中央可能有不统一,例如客户端可能提供了 DHE_DSS 密钥替换,然而 “signature_algorithms”扩大中没有DSA算法,在这类状况下,为了正确地协商,服务器必须确保满足本人抉择的CipherSuite满足 “signature_algorithms” 的限度。这不优雅,然而是为了把对原来的CipherSuite协商的设计的改变减到最小,而做的斗争。
并且,hash和签名算法,必须和服务器的证书外面的公钥兼容。
5.6. handshake — Certificate Request
TLS规定了一个可选性能:服务器能够认证客户端的身份,这通过服务器要求客户端发送一个证书实现,服务器应该在ServerKeyExchange之后立刻发送CertificateRequest音讯。
音讯构造:
enum { rsa_sign(1), dss_sign(2), rsa_fixed_dh(3),dss_fixed_dh(4), rsa_ephemeral_dh_RESERVED(5),dss_ephemeral_dh_RESERVED(6), fortezza_dms_RESERVED(20), ecdsa_sign(64), rsa_fixed_ecdh(65), ecdsa_fixed_ecdh(66), (255)} ClientCertificateType;opaque DistinguishedName<1..2^16-1>;struct { ClientCertificateType certificate_types<1..2^8-1>; SignatureAndHashAlgorithm supported_signature_algorithms<2^16-1>; DistinguishedName certificate_authorities<0..2^16-1>;} CertificateRequest;
certificate_types
: 客户端能够提供的证书类型。
- rsa_sign 蕴含RSA公钥的证书。
- dss_sign 蕴含DSA公钥的证书。
- rsa_fixed_dh 蕴含动态DH公钥的证书。
dss_fixed_dh 蕴含动态DH公钥的证书。
supported_signature_algorithms
: 服务器反对的 hash/signature 算法的列表。certificate_authorities
: 服务器能够承受的CA(certificate_authorities)的 distinguished names 的列表 DER编码格局.
这些 distinguished names 可能为root CA或者次级CA指定了想要的 distinguished name ,因而,这个音讯能够用来形容已知的root,或者心愿的受权空间。
如果 certificate_authorities 列表是空的,那么客户端能够发送任何适当的 ClientCertificateType 类型的证书,如果没有别的限度的话。
certificate_types 和 supported_signature_algorithms 字段的穿插抉择很简单。 certificate_types 这个字段从SSLv3时代就定义了,然而始终都没有具体定义,其大多数性能都被 supported_signature_algorithms 代替了。
有如下规定:
- 客户端提供的任何证书,必须用一个supported_signature_algorithms 中呈现过的 hash/signature 算法对 签名.
- 客户端提供的末端证书必须提供一个和 certificate_types 兼容的key。 如果这个key是一个签名key,那必须能和 supported_signature_algorithms 中提供的某个 hash/signature 算法对配合应用。
- 因为历史起因,某些客户端证书类型的名字,蕴含了证书的签名算法,例如,晚期版本的TLS中, rsa_fixed_dh 意思是一个被RSA算法签订,并且蕴含一个固定DH密钥的证书。在TLS1.2中,这个性能被 supported_signature_algorithms 淘汰,并且证书类型不再限度用来签订证书的算法。例如,如果服务器发送了 dss_fixed_dh 证书类型,和 { {sha1, dsa}, {sha1,rsa} } 签名类型,客户端能够回复一个 蕴含动态DH密钥,用RSA-sha1签订的证书。
- 如果协商进去的是匿名CipherSuite,服务器不能要求客户端认证。
5.7. handshake — Server Hello Done
在 ServerHello和相干音讯曾经解决完结后,服务器发送ServerHelloDone。在发送ServerHelloDone后,服务器开始期待客户端的响应。
ServerHelloDone音讯示意,服务器曾经发送完了密钥协商须要的音讯,并且客户端能够开始客户端的密钥协商解决了。
收到ServerHelloDone后,客户端应该确认服务器提供了非法的证书,并且确认服务器的ServerHello音讯外面的参数是能够承受的。
音讯格局:
struct { } ServerHelloDone;
5.8. handshake — Client Certificate
ClientCertificate音讯是客户端收到ServerHelloDone后,能够发送的第一条音讯。仅当服务器要求了一个证书的状况下,客户端才发送ClientCertificate音讯,如果没有可用的适合证书,客户端必须发送一条不蕴含任何证书的ClientCertificate音讯(即 certificate_list 构造长度为0)。
如果客户端没有发送任何证书,服务器自行决定,能够放弃要求客户端认证,持续握手;或者发送一条 fatal handshake_failure的alert音讯,断开连接。并且,如果证书链的某些方面是不能承受的(比方证书没有被可信赖的CA签订),服务器能够自行决定,是持续握手(放弃要求客户端认证),或者发送一条fatal的alert。
客户端证书应用和ServerCertificate雷同的构造发送。
ClientCertificate把客户端的证书链发送给服务器。服务器会应用证书链来验证CertificateVerify 音讯(如果应用基于签名的客户端认证),或者来计算premaster secret(对于非短暂的 DH)。证书必须和协商进去的CipherSuite的密钥替换算法配套,并和任何协商的扩大配套。
尤其是:
- 证书必须是X.509v3 类型的。
- 客户端的末级证书的公钥必须和CertificateRequest里列出的证书类型兼容。
客户端证书类型 | 证书公钥类型 |
---|---|
rsa_sign | RSA公钥;证书必须容许公钥用于certificateVerify音讯中的数字签名和hash算法 |
dss_sign | DSA 公钥;证书必须容许密钥应用CertificateVerify中的hahs函数做签名; |
ecdsa_sign | 能够用作 ECDSA 的公钥;证书必须容许 公钥用 CertificateVerify中的hash函数做签名;公钥必须应用服务器反对的曲线,和点格局; |
rsa_fixed_dh / dss_fixed_dh | Diffie-Hellman 公钥; 必须应用和服务器key雷同的参数。 |
rsa_fixed_ecdh / ecdsa_fixed_ecdh | 能够用作 ECDH 的公钥。必须和服务器的公钥应用同样的曲线,同样的点格局 |
- 如果 certificate_authorities 列表不是空的,客户端证书链中的某一个证书必须是CA中的某一个签订的。
- 证书必须应用 服务器能够承受的 hash/signature 算法对。
相似于Server Certificate,有一些证书目前无奈在TLS中应用。
5.9. handshake — Client Key Exchange
客户端必须在客户端的Certificate音讯之后,立刻发送ClientKeyExchange音讯。
或者必须在ServerHelloDone后立刻发送ClientKeyExchange音讯。
ClientKeyExchange音讯中,会设置premaster secret,通过发送 RSA公钥加密premaster secret的密文,或者发送容许单方得出雷同的premaster secret的Diffie-Hellman参数。
当客户端应用短暂的 Diffie-Hellman 密钥对时,ClientKeyExchange蕴含客户端的 Diffie-Hellman 公钥。如果客户端发送一个蕴含动态 Diffie-Hellman 指数的证书(比方,在应用固定DH的客户端认证),那么这条音讯必须被发送,并且必须为空。
音讯构造:
音讯的抉择取决于抉择的密钥替换算法。
struct { select (KeyExchangeAlgorithm) { case rsa: EncryptedPreMasterSecret; case dhe_dss: case dhe_rsa: case dh_dss: case dh_rsa: case dh_anon: ClientDiffieHellmanPublic; case ec_diffie_hellman: ClientECDiffieHellmanPublic; } exchange_keys;} ClientKeyExchange;
5.9.(1). RSA 加密的 Premaster Secret 音讯
如果用RSA做密钥协商和认证,客户端生成 48字节的 premaster secret,应用服务器证书外面的公钥加密,而后把密文EncryptedPreMasterSecret发送给服务器,构造定义如下:
struct { ProtocolVersion client_version; opaque random[46];} PreMasterSecret;client_version 客户端反对的最新协定版本号,这个字段用来检测中间人版本回退攻打。Trandom 46 字节的,平安生成的随机值。struct { public-key-encrypted PreMasterSecret pre_master_secret;} EncryptedPreMasterSecret;pre_master_secret 这个随机值由客户端生成,用于生成master secret。
注:PreMasterSecret外面的 client_version 是 ClientHello.client_version,而不是协商的到的版本号,这个个性用来阻止版本回退攻打。可怜的是,有些不正确的老的代码应用了协商失去的版本号,导致查看client_version字段的时候,和正确的实现无奈互通。
客户端实现必须在PreMasterSecret中发送正确的版本号。如果 ClientHello.client_version 的版本号是 TLS 1.1 或者更高,服务器实现必须如下查看版本号。如果版本号是 TLS 1.0 或者更早,服务器必须查看版本号,然而能够通过配置项敞开查看。
要留神的是,如果版本号查看失败了,PreMasterSecret 应该像上面形容的那样填充成随机数。
TLS中的RSA应用的是 PKCS1-V1.5 填充( PKCS1-V1.5也是openssl库RSA的默认填充形式)。Bleichenbacher 在1998年发表了一种针对 PKCS1-V1.5 的抉择密文攻打, Klima在2003年发现 PKCS1-V1.5 中 PreMasterSecret 版本号查看的一个侧通道攻打。只有TLS 服务器裸露一条特定的音讯是否合乎PKCS1-V1.5格局,或裸露PreMasterSecret解密后构造是否非法,或版本号是否非法,就能够用下面2种办法攻打。
Klima 还提出了完全避免这类攻打的办法:对格局不正确的音讯,版本号不符的状况,要做出和完全正确的RSA块一样的响应,要让客户端辨别不出这3种状况。
具体地说,要如下:
- 生成 46 字节的密码学平安随机值 R
- 解密音讯,取得明文 M
- 如果 PKCS#1 填充不正确,或者 PreMasterSecret 音讯的长度不是48字节,则
pre_master_secret = ClientHello.client_version || R
或者如果 ClientHello.client_version <= TLS 1.0,并且明确禁止了版本号查看,则
pre_master_secret = ClientHello.client_version || M[2..47]
留神:明确地用 ClientHello.client_version 结构 pre_master_secret 时,当客户端在原来的 pre_master_secret 中发送了谬误的 客户端版本值时,会产生一个不非法的 master_secret 。
另一种解决问题的办法是,把版本号不符,当成 PKCS-1 格局谬误来看待,并且齐全随机填充 premaster secret。
- 生成 48 字节的密码学平安随机值 R
- 解密 PreMasterSecret 复原出明文 M
- 如果 PKCS#1 填充不正确,或者音讯的长度不是48字节,则
pre_master_secret = R
或者如果 ClientHello.client_version <= TLS 1.0,并且 明确禁止了版本号查看,则
pre_master_secret = M
或者如果 M[0..1] != CleintHello.client_version
pre_master_secret = R
或者
pre_master_secret = M
只管实际中,还没有发现针对这种构造的攻打,Klima 在论文中形容了几种实践上的攻击方式,因而举荐上述的第一种构造。
在任何状况下,一个 TLS 服务器相对不能在:1. 解决 RSA 加密的 premaster 音讯失败, 2.或者版本号查看失败 时产生alert音讯。当遇到这两种状况时,服务器必须用随机生成的 premaster 值持续握手。服务器能够把造成失败的实在起因log下来,用于考察问题,然而必须小心确保不能把这种信息透露给攻击者(比方通过工夫侧通道,log文件,或者其它通道等透露)。
RSAES-OAEP 加密体制,更能抵制 Bleichenbacher 发表的攻打,然而,为了和晚期的TLS版本最大水平放弃兼容,TLS 依然规定应用 RSAES-PKCS1-v1_5 体制。只有恪守了下面列出的倡议,目前还没有 Bleichenbacher 的变动模式能攻破 TLS 。
实现的时候要留神:公钥加密的数据用 字节数组 <0..2^16-1> 的模式示意。因而,ClientKeyExchange中的 RSA加密的PreMasterSecret 后面有2个字节用来示意长度。这2个字节在应用RSA做密钥协商时,是冗余的,因为此时 EncryptedPreMasterSecret 是 ClientKeyExchange 中的惟一字段,因而能够无歧义地得出 EncryptedPreMasterSecret 的长度。因而更早的 SSLv3 标准没有明确规定 public-key-encrypted 数据的编码格局,因而有一些SSLv3的实现没有蕴含 长度字段,这些实现间接把 RSA 加密的数据放入了 ClientKeyExchange音讯外面。
TLS标准要求 EncryptedPreMasterSecret 字段蕴含长度字段。因而得出的后果会和一些 SSLv3 的实现不兼容。实现者从 SSLv3 降级到 TLS 时,必须批改本人的实现,以承受并且生成带长度的格局。如果一个实现要同时兼容 SSLv3 和 TLS,那就应该依据协定版本确定本人的行为。
留神:依据 Boneh 等在2003年USENIX Security Symposium上发表的论文 “Remote timing attacks are practical”,针对 TLS RSA密钥替换的近程工夫侧通道攻打,是理论可行的,起码当客户端和服务器在同一个LAN里时是可行的。因而,应用动态 RSA 密钥的实现,必须应用 RSA blinding,或者Boneh论文中提到的,其余抵制工夫侧通道攻打的技术。
openssl中的RSA blinding,参见:http://linux.die.net/man/3/rs...
5.9.(2). 客户端 Diffie-Hellman 公钥
这条音讯把客户端的 Diffie-Hellman 公钥 ( Yc ) 发送给服务器。
Yc的编码方式由 PublicValueEncoding 决定。
音讯的构造:
enum { implicit, explicit } PublicValueEncoding;implicit 如果客户端曾经发送了一个蕴含适合的 DH 公钥的证书(即 fixed_dh 客户端认证形式),那么Yc曾经隐式蕴含了,不须要再发送。这种状况下,ClientKeyExchange音讯必须发送,并且必须是空的。explicit 示意Yc须要发送。struct { select (PublicValueEncoding) { case implicit: struct { }; case explicit: opaque dh_Yc<1..2^16-1>; } dh_public;} ClientDiffieHellmanPublic;dh_Yc 客户端的 Diffie-Hellman 公钥 Yc.
5.9.(3). 客户端 EC Diffie-Hellman 公钥
struct { select (PublicValueEncoding) { case implicit: struct { }; case explicit: ECPoint ecdh_Yc; } ecdh_public;} ClientECDiffieHellmanPublic;
Diffie-Hellman 推广到椭圆曲线群上,就是 EC Diffie-Hellman ,简称 ECDH,其它的计算,和个别的 DH 计算相似。
ECDH 是目前最重要的密钥协商算法 http://vincent.bernat.im/en/b...
5.10. handshake — Cerificate Verify
当须要做客户端认证时,客户端发送CertificateVerify音讯,来证实本人的确领有客户端证书的私钥。这条音讯仅仅在客户端证书有签名能力的状况下发送(就是说,除了含有固定 Diffie-Hellman 参数的证书以外的证书)。CertificateVerify必须紧跟在ClientKeyExchange之后发送。
音讯构造:
Structure of this message:
struct { digitally-signed struct { opaque handshake_messages[handshake_messages_length]; }} CertificateVerify;
此处, handshake_messages 示意所有发送或者接管的握手音讯,从client hello开始,始终到CertificateVerify之前的所有音讯,包含handshake音讯的type和length字段,这是之前所有握手构造体的拼接。要留神,这要求单方在握手过程中,都得缓存所有音讯,或者在握手过程中,用每一种可能的hash算法计算到CeritificateVerify为止的hash值。
signature中用的hash和签名算法必须是 CertificateRequest 的 supported_signature_algorithms 中的某一种。另外,hash和签名算法必须和客户端的证书的算法兼容。
RSA公钥可能被用于任何容许的hash函数,只有遵循证书中的限度。
本文转自微信后盾团队,如有进犯,请分割咱们立刻删除
OpenIMgithub开源地址:
https://github.com/OpenIMSDK/...
OpenIM官网 : https://www.rentsoft.cn
OpenIM官方论坛: https://forum.rentsoft.cn/
更多技术文章:
开源OpenIM:高性能、可伸缩、易扩大的即时通讯架构
https://forum.rentsoft.cn/thr...
【OpenIM原创】简略轻松入门 一文解说WebRTC实现1对1音视频通信原理
https://forum.rentsoft.cn/thr...
【OpenIM原创】开源OpenIM:轻量、高效、实时、牢靠、低成本的音讯模型
https://forum.rentsoft.cn/thr...