欢迎查阅我们的技术常见问题解答。
客户端开发人员必须遵守安全准则。
本页面介绍用于云聊天(服务器-客户端加密)的 MTProto 加密基础层。另请参阅:
该协议旨在允许移动设备上的应用程序访问服务器 API。必须强调的是,Web 浏览器并非此类应用程序。
该协议分为三个几乎独立的部分:
从 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 位或pq1000p字节q的参数)则以大端序格式写入,因为 OpenSSL 库就是这样处理的。
在消息(或多部分消息)通过传输协议在网络上传输之前,它会以特定方式加密,并在消息顶部添加一个外部头部,该头部包含:一个 64 位密钥标识符(用于唯一标识服务器和用户的授权密钥)和一个 128 位消息密钥。用户密钥与消息密钥共同构成一个 256 位密钥,该密钥用于使用 AES-256 加密算法对消息进行加密。请注意,待加密消息的初始部分包含可变数据(会话、消息 ID、序列号、服务器盐值),这些数据显然会影响消息密钥(进而影响 AES 密钥和初始化向量)。消息密钥定义为消息体(包括会话、消息 ID 等)SHA256 校验和的中间 128 位(包含填充字节),并加上取自授权密钥的 32 个字节作为前缀。多部分消息会被作为一个单独的消息进行加密。
有关技术规范,请参阅《移动协议:详细描述》。
客户端应用程序必须做的第一件事是创建授权密钥,该密钥通常在首次运行时生成,并且几乎从不更改。
为了防止攻击者拦截加密消息,并通过某种方式窃取授权密钥(例如,通过窃取设备——即使在这种情况下,攻击者也可以在不解密任何内容的情况下访问设备上缓存的所有信息)事后解密加密消息,MTProto 在云聊天和秘密聊天中都支持完美前向保密。
如果客户端时间与服务器时间偏差较大,服务器可能会因为消息标识符无效(该标识符与消息创建时间密切相关)而开始忽略客户端消息,反之亦然。在这种情况下,服务器会向客户端发送一条特殊消息,其中包含正确的时间和特定的 128 位盐值(该盐值可以由客户端在特殊的 RPC 同步请求中显式提供,也可以等于当前会话期间从客户端收到的最新消息的键值)。如果时间差异显著但尚未导致客户端消息被忽略,则该消息可能是包含其他消息的容器中的第一条消息。
收到此类消息或包含此类消息的容器后,客户端首先执行时间同步(实际上只是存储服务器时间与自身时间之间的差异,以便将来能够计算“正确”时间),然后验证消息标识符的正确性。
如果忽略了某个更正,客户端将不得不生成一个新的会话,以确保消息标识符的单调性。
在使用选定的传输协议发送之前,有效负载必须包装在由适当的 MTProto 传输协议定义的辅助协议头中。
服务器通过请求头识别这些不同的协议(并将它们与 HTTP 区分开来)。
此外,还可以使用以下传输特性:
可以在tdlib和MadelineProto中看到这些协议的示例实现。
支持将加密容器及其外部头部(以下简称“有效载荷”)从客户端传输到服务器,反之亦然。
定义了多种传输协议:
(我们只探讨前五种类型。)
综上所述,以ISO/OSI 标准栈为例进行比较: