请随时查看我们针对技术人员的常见问题解答。客户端开发人员必须遵守安全指南。
本页面介绍用于云聊天的 MTProto 加密的基本层(服务器-客户端加密)。也可以看看:
该协议旨在从移动设备上运行的应用程序访问服务器 API。必须强调的是,网络浏览器不是这样的应用程序。
该协议分为三个几乎独立的部分:
从版本 4.6 开始,主要的 Telegram 客户端正在使用 MTProto 2.0,如本文所述。 MTProto v1.0(此处描述仅供参考)已弃用,目前正在逐步淘汰。
从高级组件的角度来看,客户端和服务器在会话内交换消息。会话附加到客户端设备(更准确地说是应用程序),而不是特定的 WebSocket/http/https/tcp 连接。此外,每个会话都附加一个用户密钥ID,通过该ID 实际完成授权。
可能会打开多个与服务器的连接;消息可以通过任何连接以任一方向发送(对查询的响应不一定通过承载原始查询的同一连接返回,尽管最常见的是这种情况;但是,在任何情况下都不能发送消息)通过属于不同会话的连接返回)。当使用 UDP 协议时,响应可能会由与查询发送到的 IP 地址不同的 IP 地址返回。
消息有几种类型:
从较低层协议的角度来看,消息是沿 4 或 16 字节边界对齐的二进制数据流。消息中的前几个字段是固定的并由加密/授权系统使用。
每条消息,无论是单独的还是在容器内,都由消息标识符(64 位,见下文)、会话中的消息序列号(32 位)、长度(消息正文以字节为单位;32 位)和主体(任何大小,4 字节的倍数)。此外,当发送容器或单个消息时,会在顶部添加一个内部标头(见下文),然后对整个消息进行加密,并在消息顶部放置一个外部标头(一个 64 位的密钥标识符和 128 位消息密钥)。
消息正文通常由 32 位消息类型和后跟与类型相关的参数组成。特别地,每个RPC函数都有对应的消息类型。有关更多详细信息,请参阅二进制数据序列化、移动协议:服务消息。
所有数字均以小端方式写入。然而,RSA 和 DH 中使用的非常大的数字(2048 位或 pq、p、q 参数)以大端格式写入,因为 OpenSSL 库就是这样做的。
在使用传输协议通过网络传输消息(或多部分消息)之前,会以某种方式对其进行加密,并在消息顶部添加外部标头,即: 64 位密钥标识符(唯一标识服务器和用户的授权密钥)和 128 位消息密钥。用户密钥与消息密钥一起定义了一个实际的 256 位密钥,该密钥使用 AES-256 加密来加密消息。请注意,要加密的消息的初始部分包含可变数据(会话、消息 ID、序列号、服务器盐),这些数据显然会影响消息密钥(从而影响 AES 密钥和 iv)。消息密钥定义为消息正文(包括会话、消息 ID 等)的 SHA256 的 128 个中间位,包括填充字节,前面加上取自授权密钥的 32 个字节。多部分消息被加密为单个消息。
有关技术规范,请参阅移动协议:详细说明
客户端应用程序必须做的第一件事是创建一个授权密钥,该密钥通常在首次运行时生成,并且几乎永远不会更改。
为了防止攻击者通过某种方式盗用授权密钥来拦截加密消息并在事后解密它们(例如,通过窃取设备 - 即使在这种情况下,人们也可以在不解密任何内容的情况下访问设备上缓存的所有信息), MTProto 在云聊天和秘密聊天中都支持完美前向保密。
如果客户端时间与服务器时间相差很大,则服务器可能会开始忽略客户端消息,反之亦然,因为消息标识符无效(与创建时间密切相关)。在这种情况下,服务器将向客户端发送一条特殊消息,其中包含正确的时间和某个 128 位盐(要么由客户端在特殊的 RPC 同步请求中显式提供,要么等于从客户端收到的最新消息的密钥)在当前会话期间)。该消息可能是包含其他消息的容器中的第一个消息(如果时间差异很大但尚未导致客户端的消息被忽略)。
收到这样的消息或保存该消息的容器后,客户端首先执行时间同步(实际上,只是存储服务器时间与其自身时间之间的差异,以便能够计算将来的“正确”时间),然后验证消息标识符的正确性。
在忽略纠正的情况下,客户端将必须生成新会话以确保消息标识符的单调性。
在使用选定的传输协议发送之前,有效负载必须包装在由适当的 MTProto 传输协议定义的辅助协议标头中。
服务器通过标头识别这些不同的协议(并将它们与 HTTP 区分开来)。此外,还可以使用以下传输功能:
这些协议的示例实现可以在 tdlib 和 MadelineProto 中看到。
允许将加密容器与外部标头(以下称为有效负载)一起从客户端传送到服务器并返回。定义了多种传输协议:
(我们将只检查前五种类型。)
回顾一下,使用 ISO/OSI 堆栈作为比较: