Telegram 护照

Telegram Passport是一种统一的身份验证方式,适用于需要个人身份验证的服务。用户只需上传一次证件,即可立即与需要真实身份证明的服务(例如金融、ICO 等)共享数据。由于采用了端到端加密,Telegram 无法访问用户的个人信息。

本页描述了客户端应用程序必须使用的请求流程,以便将请求的数据发送到服务。

概述

从需要真实身份证明的服务的角度来看,流程如下:

请参阅“以机器人身份”了解如何通过 MTProto API 使用机器人请求护照数据。请参阅“护照手册”了解如何通过简化的机器人 API 使用机器人请求护照数据。

从用户的角度来看,这个过程大致如下:

请参阅“作为用户”部分,了解用户客户端应用程序应如何通过 MTProto API 将护照数据发送到服务。

棍子

可以使用机器人 API 来简化此流程,更多信息请参阅护照手册。

使用 MTProto API 的过程基本相同,直到实际的 API 调用为止。

请注意,所有二进制字段均采用原始二进制格式,这与机器人 API 中采用 Base64 编码的字段不同。

设置 Telegram Passport

根据机器人 API。

索取信息

根据机器人 API。

接收信息

模式:

secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;securePlainPhone#7d6099dd phone:string = SecurePlainData; securePlainEmail#21ec5a5f email:string = SecurePlainData;secureFile#7d09c27e id:long access_hash:long size:long dc_id:int date:int file_hash:bytes secret:bytes = SecureFile;secureValueTypePersonalDetails#9d2a81e3 = SecureValueType; secureValueTypePassport#3dac6a00 = SecureValueType; secureValueTypeDriverLicense#6e425c4 = SecureValueType; secureValueTypeIdentityCard#a0d0744b = SecureValueType; secureValueTypeInternalPassport#99a48f23 = SecureValueType; secureValueTypeAddress#cbe31e26 = SecureValueType; secureValueTypeUtilityBill#fc36954e = SecureValueType; secureValueTypeBankStatement#89137c0d = SecureValueType; secureValueTypeRentalAgreement#8b883488 = SecureValueType; secureValueTypePassportRegistration#99e3806a = SecureValueType; secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType; secureValueTypePhone#b320aadb = SecureValueType; secureValueTypeEmail#8e3ca7ee = SecureValueType;secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector<SecureFile> files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction; messageService#7a800e0a flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;

当用户按下“授权”按钮确认您的请求时,MTProto API 会从用户发送updateNewMessage ,其中包含messageService构造函数,该构造函数包含messageActionSecureValuesSentMe构造函数,其中包含加密的 Telegram Passport 数据。

解密数据

secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;

要解密接收到的数据,首先解密secureCredentialsEncrypted中包含的凭据。

  1. 使用您的私钥解密凭据密钥(secureCredentialsEncrypted中的secret字段)(例如,在 PHP 中设置 OAEP 填充选项)。OPENSSL_PKCS1_OAEP_PADDING

  2. 使用此密钥和凭据哈希(secureCredentialsEncrypted中的哈希字段)计算credentials_key和credentials_iv,如下所述:

    credentials_secret_hash = SHA512( credentials_secret + credentials_hash )credentials_key = slice( credentials_secret_hash, 0, 32 )credentials_iv = slice( credentials_secret_hash, 32, 16 )
  3. 使用credentials_key和credentials_iv,通过 AES256-CBC加密解密凭据数据(secureCredentialsEncrypted中的data字段)。重要提示:在此步骤中,请确保凭据哈希值等于SHA256( credentials_data )

  4. 凭证数据会填充 32 到 255 个随机填充字节,以确保其长度能被 16 字节整除。第一个字节包含填充字节的长度(包括当前字节)。移除填充字节即可获取数据。

请注意,所有哈希值都是原始二进制数据,而不是十六进制数据。

证书

凭证是一个 JSON 序列化对象,其结构与机器人 API中的结构完全相同。由于解密凭证采用端到端加密,应用程序必须将解密凭证存储为 JSON 格式,而不是 TL 有效负载。

按照Passport 手册中的说明,凭证用于解密附加到secureValue 的文件。在本例中,端到端加密数据的容器采用 TL 格式,而加密数据本身采用 JSON 格式。

secureValue
secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector<SecureFile> files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;

secureValue构造函数的架构定义了每个字段中可以找到的构造函数。

姓名 类型 描述
类型 安全值类型 安全护照价值类型
数据 标志.0?安全数据 加密Telegram 护照元素数据
正面 标志.1?安全文件 加密护照文件,附文件正面
反面 标志.2?安全文件 加密护照文件,背面为文件内容
自拍 标志.3?安全文件 加密护照文件,内含用户手持该文件的自拍照
翻译 标志.6?向量<SecureFile> 一组加密的护照文件,以及所提供文件的翻译版本。
文件 标志.4?向量<SecureFile> 一组包含照片的加密护照文件
普通数据 标志.5?安全纯文本数据 纯文本已验证护照数据
哈希 字节 数据哈希

以下是可能的SecureValueTypes列表,以及使用每种类型时可以设置/请求的参数。

类型 允许的字段
secureValueTypeEmail plain_data
安全值类型电话 plain_data
secureValueTypePersonalDetails data
安全值类型护照 data,,,front_sideselfietranslation
安全值类型驾驶许可证 data,,,,front_sidereverse_sideselfietranslation
安全值类型身份卡 data,,,,front_sidereverse_sideselfietranslation
secureValueTypeInternalPassport data,,,front_sideselfietranslation
安全值类型地址 data
secureValueTypeUtilityBill files,translation
安全值类型银行声明 files,translation
安全价值类型租赁协议 files,translation
安全值类型护照注册 files,translation
安全值类型临时注册 files,translation
安全数据
secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;

数据是经过加密和填充的 JSON 序列化对象,其类型为指定的 JSON 类型之一,具体取决于所选类型。

选定类型 JSON 对象
secureValueTypePersonalDetails 个人信息
安全值类型护照 身份文档数据
安全值类型驾驶许可证 身份文档数据
安全值类型身份卡 身份文档数据
secureValueTypeInternalPassport 身份文档数据
安全值类型地址 住宅地址

从凭证中提取的DataCredentials可用于解密secureData中数据字段的加密数据。有关如何解密数据字段的更多信息,请参阅Passport 手册。

SecureFile
secureFile#7d09c27e id:long access_hash:long size:long dc_id:int date:int file_hash:bytes secret:bytes = SecureFile;inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;---functions---upload.getFile#be5335be flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:long limit:int = upload.File;

文件(解密后为 JPG 格式,最大 10 MB)将按块下载,如文件 »中所述,但需要注意的是,应该生成一个inputFileLocation,而不是生成inputFileLocation 。

从凭据中提取的文件凭据可用于解密下载的加密数据。有关如何解密 Passport 文件的更多信息,请参阅Passport 手册。

安全纯数据
securePlainPhone#7d6099dd phone:string = SecurePlainData; securePlainEmail#21ec5a5f email:string = SecurePlainData;

电子邮件地址/电话号码以明文形式通过相应的SecurePlainData构造函数传递。使用 Telegram Passport 发送的电子邮件地址和电话号码已按照Passport 手册中的说明进行验证。

修复错误

secureValueErrorData#e8a40bd9 type:SecureValueType data_hash:bytes field:string text:string = SecureValueError; secureValueErrorFrontSide#be3dfa type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError; secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError; secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;inputUser#f21158c6 user_id:long access_hash:long = InputUser;---functions---users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool;

如果收到的数据有误,机器人可以使用users.setSecureValueErrors方法通知用户并重新请求信息。在所有错误修复之前,用户将无法重新发送数据。

可以在方法的文档页面中找到方法参数的说明»。

作为用户

接收请求

当您的应用收到来自 SDK或自定义源的事件时,该过程就开始了。

URI格式

SDK 通过打开以下深层链接触发 Passport 授权请求 »:

tg:句法:

tg://passport?bot_id=<bot_user_id>&scope=<scope>&public_key=<public_key>&nonce=<nonce>tg://resolve?domain=telegrampassport&bot_id=<bot_user_id>&scope=<scope>&public_key=<public_key>&nonce=<nonce>

使用以下查询字符串参数:

参数 类型 必需的 描述
领域 细绳 仅某些resolve版本的护照URI需要此项 始终telegrampassport用于护照授权申请。
机器人ID 整数 是的 机器人的唯一标识符。您可以从机器人令牌中获取它。例如,对于机器人令牌1234567:4TT8bAc8GHUspu3ERYn-KGcvsvGB9u_n4ddy,机器人 ID 为1234567。
范围 UriPassportScope 是的 一个更简洁的 JSON 序列化对象,用于描述您要请求的数据。
公钥 细绳 是的 机器人的公钥
nonce 细绳 是的 机器人指定的随机数。重要提示:出于安全考虑,它应该是请求的加密安全唯一标识符。尤其需要注意的是,它应该足够长,并且应该使用加密安全的伪随机数生成器生成。切勿两次接受具有相同随机数的凭据。
回调 URL 细绳 选修的 某些 Telegram 客户端支持此功能,可指定在进程完成或取消后要打开的回调 URL。
有效载荷 细绳 选修的 Telegram Passport 1.0 中已弃用的参数,其功能与之前的参数相同nonce。
仍然使用旧版 SDK 的服务可能会提供此参数来代替之前的参数nonce。
在某些情况下,为了向后兼容,URI 中可能会同时出现这两个参数:在这种情况下,nonce应始终使用之前的参数,而不是之前的参数。payloadnoncepayload

示例 URI,由Telegram Passport 示例页面生成:

tg://resolve?domain=telegrampassport&bot_id=543260180&scope=%7B%22v%22%3A1%2C%22d%22%3A%5B%7B%22_%22%3A%22pd%22%2C%22n%22%3A1%7D%2C%22ad%22%2C%22pn%22%2C%22em%22%2C%7B%22_%22%3A%5B%7B%22_%22%3A%22pp%22%2C%22s%22%3A1%2C%22t%22%3A1%7D%2C%22ip%22%2C%22dl%22%2C%22ic%22%5D%7D%2C%7B%22_%22%3A%5B%22ub%22%2C%22bs%22%2C%22ra%22%2C%22pr%22%2C%22tr%22%5D%7D%5D%7D&public_key=-----BEGIN%20PUBLIC%20KEY-----%0AMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv6m1zBF8lZOCqcxf8hnj%0AkvHwuWdU8s4rBWaxKXH%2FvDDUklcCS5uhSnmjhxWca9suubaG3lW4HxlCilkeJPVf%0Ajimg5Q8ZqWrR3OoOihEpcG9iJZTOEpsEk7VtEiabgacBG3Quv9JslTrDe95Fn801%0At9d21HXwgMrHeHpWDOn31Dr%2BwoEH%2BkwySUWa6L%2FZbnGwSNP7eeDTE7Amz1RMDk3t%0A8EWGq58u0IQatPcEH09aUQlKzk6MIiALkZ9ILBKCBk6d2WCokKnsdBctovNbxwSx%0AhP1qst1r%2BYc8iPBZozsDC0ZsC5jXCkcODI3OC0tkNtYzN2XKalW5R0DjDRUDmGhT%0AzQIDAQAB%0A-----END%20PUBLIC%20KEY-----%0A&nonce=b8e892dc2e0afe63424d101b964f1256_32858210_708614a4585b84872e&callback_url=https%3A%2F%2Fcore.telegram.org%2Fpassport%2Fexample%3Fpassport_ssid%3Db8e892dc2e0afe63424d101b964f1256_32858210_db259b427f200751ce&payload=b8e892dc2e0afe63424d101b964f1256_32858210_708614a4585b84872e

UriPassportScope

该对象表示要请求的数据。

场地 类型 描述
d UriPassportScopeElement数组 请求元素的列表,每种类型在整个 UriPassportScopeElement 对象数组中只能使用一次。
整数 范围版本必须为1
UriPassportScopeElement

此对象表示请求的元素,应该是以下之一:

护照文件类型标识符与以下简化类型标识符具有别名:

满的 别名
personal_details pd
passport pp
driver_license dl
identity_card ic
internal_passport ip
id_document idd
address ad
utility_bill ub
bank_statement bs
rental_agreement ra
passport_registration pr
temporary_registration tr
address_document add
phone_number pn
email em

您可以将特殊类型“idd”用作“pp”、“dl”、“ic”之一的别名,将特殊类型“add”用作“ub”、“bs”、“ra”之一的别名。

UriPassportScopeElementOneOfSeveral

该对象代表多个元素,其中必须提供其中一个。

场地 类型 描述
_ UriPassportScopeElementOne数组 元素列表,其中必须包含一个元素;必须包含“pp”、“dl”、“ic”、“ip”中的多个元素,或者包含“ub”、“bs”、“ra”、“pr”、“tr”中的多个元素。
s 布尔值 可选。如果您想请求用户与此列表中选择上传的文档进行自拍照,请使用此参数。
t 布尔值 可选。如果您希望翻译用户从列表中选择上传的文档,请使用此参数。注意:我们建议仅在收到需要翻译的有效文档后才请求翻译。

UriPassportScopeElementOne

此对象代表必须提供的特定元素。如果不需要任何选项,则可以使用字符串代替此对象来指定元素的类型。

场地 类型 描述
_ 细绳 元素类型。可以是以下之一:"pd", "pp", "dl", "ic", "ip", "ad", "ub", "bs", "ra", "pr", "tr", "pn", "em"
s 布尔值 可选。如果您还想请求与证件的自拍照,请使用此参数。适用于“pp”、“dl”、“ic”和“ip”。
t 布尔值 可选。如果您还需要翻译文档,请使用此参数。适用于“pp”、“dl”、“ic”、“ip”、“ub”、“bs”、“ra”、“pr”和“tr”等关键词。注意:我们建议您仅在收到需要翻译的有效文档后才请求翻译。
n 布尔值 可选。使用此参数可以请求用户以其居住国家/地区的语言显示的姓名(包括名字、姓氏和中间名)。适用于“pd”

您还可以使用特殊类型“idd”作为“pp”、“dl”、“ic”之一的别名,使用特殊类型“add”作为“ub”、“bs”、“ra”之一的别名。

设置 Telegram Passport

客户端应用程序的下一步是请求用户的 2FA 护照,并配置 Telegram Passport/获取和解密远程保存的 Telegram Passport 参数,如加密文章中所述 »。

领取护照表格

account.authorizationForm#ad2e1cd8 flags:# required_types:Vector<SecureRequiredType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;---functions---account.getAuthorizationForm#a929597a bot_id:long scope:string public_key:string = account.AuthorizationForm;

然后,客户端应用程序使用account.getAuthorizationForm方法将来自passport 授权请求的机器人 ID、范围和公钥传递给 Telegram 服务器。

响应将是一个account.authorizationForm构造函数,其中包含所需文档类型、服务隐私政策的 URL 以及表单应发送到的机器人信息。如果表单已提交过至少一次,则构造函数还会包含已提交数据的列表以及可能存在的错误。

用户应接受隐私政策并继续填写所需数据,客户端应按照加密文章中所述对其进行加密并上传»。

提交护照申请表

secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;---functions---account.acceptAuthorization#f3ed4c73 bot_id:long scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;

用户上传完所需文件并点击提交按钮后,客户端会调用account.acceptAuthorization,将文件提交给与该服务关联的机器人。

最后,客户端打开回调 URL(如果存在)。

处理无效表单

secureValueErrorData#e8a40bd9 type:SecureValueType data_hash:bytes field:string text:string = SecureValueError; secureValueErrorFrontSide#be3dfa type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError; secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError; secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;account.authorizationForm#ad2e1cd8 flags:# required_types:Vector<SecureRequiredType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;---functions---account.getAuthorizationForm#a929597a bot_id:long scope:string public_key:string = account.AuthorizationForm;

如果服务拒绝了提交表单中的任何值,机器人将调用相应的方法来设置有关错误的信息。

用户可以直接从服务中了解这些错误,或者,如果他们决定重新启动该过程并重新发送更正后的数据,则可以直接从授权表单(errors字段)中了解这些错误。