电报的安全开源策略 | AI生成和翻译

Home 2026.05

以下是 Telegram 安全开源其代码的完整解析:


1. API 密钥 — 有意提交至仓库

Telegram API ID 和哈希值故意设为公开,直接保存在仓库中:

// build-system/appstore-configuration.json
{
    "bundle_id": "ph.telegra.Telegraph",
    "api_id": "8",
    "api_hash": "7245de8e747a0d6fbe11f7cc14fcc0bb",
    "team_id": "C67CF9S4VU",
    "appstore_id": "686449807"
}

同样也存在于示例 Bazel 配置中:

// build-system/example-configuration/variables.bzl
telegram_api_id = "8"
telegram_api_hash = "7245de8e747a0d6fbe11f7cc14fcc0bb"

为何安全: Telegram 的 API 采用挑战-响应协议(MTProto)。api_idapi_hash 是公共标识符——它们向 Telegram 服务器标识你的应用,类似于客户端 ID,并非用于身份验证。身份验证通过手机号 + 短信验证码 + MTProto 密钥交换完成。所有第三方 Telegram 客户端(Pyrogram、Telethon 等)都使用相同的值。甚至有一个其他客户端的 公开密钥列表

第三方开发者的模板使用占位符:

// build-system/template_minimal_development_configuration.json
{
    "api_id": "{! 请前往 https://my.telegram.org/apps 获取 !}",
    "api_hash": "{! 请前往 https://my.telegram.org/apps 获取 !}",
    "team_id": "{! 请查阅 README.md !}"
}

2. 代码签名 — 加密 Git 仓库 + 环境密码

这是真正的密钥管理机制。配置文件描述文件和证书存储在一个独立的私有 Git 仓库(即 “fastlane match” 仓库)中,并使用 AES-256 加密。

构建系统在构建时获取并解密它们:

# build-system/Make/Make.py 第 422 行
password = os.getenv('TELEGRAM_CODESIGNING_GIT_PASSWORD')
if password is None:
    print('TELEGRAM_CODESIGNING_GIT_PASSWORD 环境变量未设置')
    sys.exit(1)

private_key = os.getenv('TELEGRAM_CODESIGNING_PRIVATE_KEY')

profile_source = GitCodesigningSource(
    repo_url=arguments.gitCodesigningRepository,  # 例如 git@gitlab.com:peter-iakovlev/fastlanematch.git
    private_key=private_key,
    team_id=build_configuration.team_id,
    bundle_id=build_configuration.bundle_id,
    codesigning_type=arguments.gitCodesigningType,  # development, adhoc, appstore, enterprise
    password=password,
    always_fetch=not arguments.gitCodesigningUseCurrent
)

加密仓库被克隆后,每个 .mobileprovision.cer.p12 文件都会被解密:

# build-system/Make/BuildConfiguration.py
def decrypt_codesigning_directory_recursively(source_base_path, destination_base_path, password):
    for file_name in os.listdir(source_base_path):
        source_path = source_base_path + '/' + file_name
        destination_path = destination_base_path + '/' + file_name
        allowed_file_extensions = ['.mobileprovision', '.cer', '.p12']
        if os.path.isfile(source_path) and any(source_path.endswith(ext) for ext in allowed_file_extensions):
            decrypt_match_data(source_path, destination_path, password)
        elif os.path.isdir(source_path):
            os.makedirs(destination_path, exist_ok=True)
            decrypt_codesigning_directory_recursively(source_path, destination_path, password)

3. 自定义 AES-256-GCM 解密(无 OpenSSL 依赖)

他们用纯 Python 编写了自己的 AES-256 实现来解密代码签名数据——同时支持 V1(AES-CBC,OpenSSL 遗留格式)和 V2(AES-GCM,fastlane match 现代格式):

# build-system/Make/DecryptMatch.py
_V1_PREFIX = b"Salted__"
_V2_PREFIX = b"match_encrypted_v2__"

def _decrypt_stored(stored_data, password):
    if stored_data.startswith(_V2_PREFIX):
        salt = stored_data[20:28]
        auth_tag = stored_data[28:44]
        ciphertext = stored_data[44:]
        material = hashlib.pbkdf2_hmac(
            'sha256',
            password.encode('utf-8'),
            salt,
            10_000,                    # 10k PBKDF2 迭代次数
            dklen=32 + 12 + 24,       # 256位密钥 + 96位IV + 192位AAD
        )
        key = material[0:32]
        iv = material[32:44]
        aad = material[44:68]
        return _aes_gcm_decrypt(ciphertext, key, iv, aad, auth_tag)

    if stored_data.startswith(_V1_PREFIX):
        salt = stored_data[8:16]
        ciphertext = stored_data[16:]
        key, iv = _evp_bytes_to_key(password, salt, 'md5', 32, 16)
        return _aes_cbc_decrypt(ciphertext, key, iv)

V2 使用 PBKDF2-HMAC-SHA256 进行密钥派生(迭代次数 10,000 次),然后使用 AES-256-GCM 进行认证加密。GCM 认证标签可防止篡改。


4. 构建变量注入 — 构建时生成的 Bazel variables.bzl

配置 JSON 由 BuildConfiguration.py 解析并写入 Bazel .bzl 文件,该文件在构建时生成,不提交到仓库

# build-system/Make/BuildConfiguration.py
def write_to_variables_file(self, bazel_path, use_xcode_managed_codesigning, aps_environment, path):
    string = ''
    string += 'telegram_api_id = "{}"\n'.format(self.api_id)
    string += 'telegram_api_hash = "{}"\n'.format(self.api_hash)
    string += 'telegram_team_id = "{}"\n'.format(self.team_id)
    string += 'telegram_is_internal_build = "{}"\n'.format(self.is_internal_build)
    # ... 写入 Bazel 导入的文件

然后 BUILD 文件将这些值作为编译器标志(而非运行时值)注入:

# submodules/BuildConfig/BUILD
load("@build_configuration//:variables.bzl", "telegram_api_id", "telegram_api_hash", ...)

objc_library(
    name = "BuildConfig",
    copts = [
        "-DAPP_CONFIG_API_ID={}".format(telegram_api_id),
        "-DAPP_CONFIG_API_HASH=\\\"{}\\\"".format(telegram_api_hash),
        "-DAPP_CONFIG_IS_INTERNAL_BUILD={}".format(telegram_is_internal_build),
        "-DAPP_CONFIG_IS_APPSTORE_BUILD={}".format(telegram_is_appstore_build),
    ],
)

API 哈希值在编译时以 C 字符串字面量的形式嵌入到二进制文件中——运行时不会从文件读取。


5. 三种代码签名来源 — 任选路径

构建系统支持三种方式提供签名身份:

# build-system/Make/Make.py
if arguments.gitCodesigningRepository is not None:
    # 路径 A:加密的 Git 仓库(Telegram 内部 CI)
    profile_source = GitCodesigningSource(...)
elif arguments.codesigningInformationPath is not None:
    # 路径 B:本地目录(开发者自己的证书)
    profile_source = DirectoryCodesigningSource(...)
elif arguments.xcodeManagedCodesigning:
    # 路径 C:Xcode 自动签名(最简单,适合模拟器/开发)
    profile_source = XcodeManagedCodesigningSource()

路径 A 是 Telegram 内部使用的方式——即加密的 fastlanematch 仓库。路径 B 适用于拥有自己 Apple Developer 证书的开发者。路径 C 适用于仅模拟器构建(无需签名)。

对于仅模拟器构建,可以完全跳过代码签名:

python3 build-system/Make/Make.py --overrideXcodeVersion \
    generateProject \
    --configurationPath=build-system/template_minimal_development_configuration.json \
    --disableProvisioningProfiles    # <-- 此标志

6. 环境变量 — 实际的机密信息

唯一真正的机密信息是:

机密信息 存放位置 保护对象
TELEGRAM_CODESIGNING_GIT_PASSWORD ~/.zshrc(开发者机器) 解密代码签名仓库
TELEGRAM_CODESIGNING_PRIVATE_KEY 环境变量(CI) 代码签名仓库的 SSH 密钥
Apple Developer 证书(.p12 加密存储在代码签名仓库中 代码签名身份
配置文件描述文件(.mobileprovision 加密存储在代码签名仓库中 应用分发授权

这些信息均不在开源仓库中。.gitignore 甚至无需列出它们,因为它们位于完全独立的仓库中。


7. build-input/ 目录 — 生成而非提交

build-input/ 目录被列入 .gitignore

build-input/*

这是构建系统写入以下内容的目录:

该目录是临时性的——每次构建时全新生成,从不提交。


总结:安全模型

┌─────────────────────────────────────────────────────────┐
│  开源仓库(Telegram-iOS)                                │
│                                                         │
│  ✅ api_id, api_hash — 公开,有意为之                    │
│  ✅ 所有源代码 — 开源                                    │
│  ✅ 构建系统(Bazel, Make.py)— 开源                    │
│  ✅ AES 解密代码 — 开源                                  │
│  ✅ 带占位符的模板配置                                    │
│  ❌ 无签名证书,无配置文件描述文件                        │
│  ❌ 无密码,无 SSH 密钥                                  │
│  ❌ 无 build-input/ 目录                                │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  私有仓库(fastlanematch)                               │
│                                                         │
│  🔒 加密的 .mobileprovision 文件(AES-256-GCM)          │
│  🔒 加密的 .p12 证书                                     │
│  🔒 加密的 .cer 文件                                      │
│  🔒 仅通过 TELEGRAM_CODESIGNING_GIT_PWD 解密            │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  开发者机器 / CI                                         │
│                                                         │
│  🔑 ~/.zshrc 中的 TELEGRAM_CODESIGNING_GIT_PASSWORD      │
│  🔑 TELEGRAM_CODESIGNING_PRIVATE_KEY(SSH 密钥)          │
│  🔑 Apple Developer 账号                                 │
└─────────────────────────────────────────────────────────┘

核心要点:代码可以安全开源,因为机密信息不在代码中。API 密钥按协议设计为公开。签名证书位于独立的加密仓库中。解密密码保存在开发者的 shell 环境中。构建系统在构建时协调这一切,生成永不提交的临时文件。


Back Donate