Telegram 护照
Telegram Passport是一种统一的身份验证方式,适用于需要个人身份验证的服务。用户只需上传一次证件,即可立即与需要真实身份证明的服务(例如金融、ICO 等)共享数据。由于采用了端到端加密,Telegram 无法访问用户的个人信息。
本页描述了客户端应用程序必须使用的请求流程,以便将请求的数据发送到服务。
概述
从需要真实身份证明的服务的角度来看,流程如下:
- 用户在您的网站或应用程序中点击“使用 Telegram 登录”。
- 您请求所需的数据。
- 用户接受您的隐私政策并同意共享其数据。
- 用户的 Telegram 应用会从 Telegram 端到端加密存储中下载并解密您请求的数据。
- 如果您请求的某些数据缺失,用户可以在此时将其添加到他们的 Telegram Passport 中。
- 用户的应用程序会使用您的公钥对数据进行加密,然后将其发送给您。
- 您解密数据,检查错误,并重新请求任何缺失或无效的信息。
- 您为用户注册了您的服务。大功告成!
请参阅“以机器人身份”了解如何通过 MTProto API 使用机器人请求护照数据。请参阅“护照手册”了解如何通过简化的机器人 API 使用机器人请求护照数据。
从用户的角度来看,这个过程大致如下:
- 您的应用从某个SDK或自定义来源接收事件/意图。
- 用户接受您的隐私政策并同意共享其数据。
- 用户的 Telegram 应用会从 Telegram 的端到端加密存储中下载您请求的数据。
- 如果您请求的某些数据缺失,用户可以在此时将其添加到他们的 Telegram Passport 中。
- 用户的应用程序使用您的公钥对数据进行加密,然后将其发送给服务。
- 您为用户注册了您的服务。大功告成!
请参阅“作为用户”部分,了解用户客户端应用程序应如何通过 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中包含的凭据。
-
使用您的私钥解密凭据密钥(secureCredentialsEncrypted中的secret字段)(例如,在 PHP 中设置 OAEP 填充选项)。OPENSSL_PKCS1_OAEP_PADDING
-
使用此密钥和凭据哈希(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 ) -
使用credentials_key和credentials_iv,通过 AES256-CBC加密解密凭据数据(secureCredentialsEncrypted中的data字段)。重要提示:在此步骤中,请确保凭据哈希值等于SHA256( credentials_data )
-
凭证数据会填充 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 。
- 该id字段是secureFileid的字段
- 该access_hash字段是secureFileaccess_hash的字段
从凭据中提取的文件凭据可用于解密下载的加密数据。有关如何解密 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
此对象表示请求的元素,应该是以下之一:
- UriPassportScopeElementOneOfSeveral- 用于请求范围内包含的任何一个文档。
- UriPassportScopeElementOne– 用于请求一个特定的文档。
护照文件类型标识符与以下简化类型标识符具有别名:
| 满的 | 别名 |
|---|---|
| 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 |
| 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,将文件提交给与该服务关联的机器人。
- 与之前一样bot_id,scope和public_key均取自授权请求 URI。
- value_hashes服务器使用该字段来选择要发送给机器人的文档类型:该type字段应设置为文档类型,该hash字段应设置为在上传加密数据时生成的data_hash/ »。file_hash
- credentials包含服务解密发送的 E2E 加密安全值所需的加密凭证:它是按照Passport Credentials »中所述生成的。
最后,客户端打开回调 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字段)中了解这些错误。