You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PHP中正确验证TON Proof?当前实现签名错误

PHP实现TON Proof验证的正确方案

问题描述

我需要用PHP验证TON Proof,官方给出的验证方案如下:

message = utf8_encode("ton-proof-item-v2/") ++ 
          Address ++
          AppDomain ++
          Timestamp ++  
          Payload 
signature = Ed25519Sign(privkey, sha256(0xffff ++ utf8_encode("ton-connect") ++ sha256(message)))

其中各字段要求:

  • Address:钱包地址,需编码为:
    • workchain:32位有符号大端序整数;
    • hash:256位无符号大端序整数;
  • AppDomain:格式为Length ++ EncodedDomainName
    • Length:UTF-8编码的应用域名的字节长度,32位数值
    • EncodedDomainName:长度为Length字节的UTF-8编码应用域名
  • Timestamp:签名操作的64位Unix时间戳
  • Payload:可变长度二进制字符串。

我写了这段代码,但生成的签名不正确:

$pubKey = "05264d80c7ad5a953cf56cdecf68ec******";
$address = "0:312ed56825e7455a7948c34677e418e6c371d263c0fd7*****";
$payload = "d0143e296879ed8f0000000064e328d8d751ea7c3c44827c8b6fcd5c83efcbfa";
$domainLengthBytes = 21;
$domainValue = "ton-connect.github.io";
$timestamp = 1692608455;
$message = utf8_encode("ton-proof-item-v2/").$address.$domainLengthBytes.$domainValue.$timestamp.$payload;
$signature = sodium_crypto_sign_detached($message,$pubKey);
print_r($message);

错误分析

你的代码存在几个核心问题:

  1. 地址处理错误:直接拼接字符串格式的地址,没有按要求将workchain和hash编码为大端序二进制数据
  2. AppDomain格式错误:直接拼接数字长度,没有将长度编码为32位大端序二进制
  3. Timestamp格式错误:直接拼接数字,没有编码为64位大端序二进制
  4. Payload处理错误:直接拼接十六进制字符串,没有转换为二进制数据
  5. 签名流程错误:没有按照官方要求对message做两次SHA256嵌套哈希,也没有添加前缀0xffff ++ utf8_encode("ton-connect")
  6. 密钥格式错误:sodium库要求密钥是二进制格式,不是十六进制字符串

正确实现步骤

  1. 解析并编码钱包地址:将0:xxxx格式的地址拆分为workchain和hash,分别编码为32位有符号大端序、256位无符号大端序二进制
  2. 编码AppDomain:计算域名的UTF-8字节长度,将长度编码为32位大端序二进制,再拼接域名的UTF-8二进制数据
  3. 编码Timestamp:将Unix时间戳编码为64位大端序二进制
  4. 转换Payload:将十六进制字符串的Payload转换为二进制数据
  5. 组装原始message:按官方顺序拼接所有二进制部分
  6. 计算嵌套哈希:先对message做SHA256,再拼接0xffff、ton-connect的UTF-8编码,再做一次SHA256
  7. 验证签名:使用sodium库的crypto_sign_verify_detached方法,传入签名、哈希后的内容和二进制公钥

完整代码实现

<?php
// 输入参数
$pubKeyHex = "05264d80c7ad5a953cf56cdecf68ec******"; // 替换为实际公钥十六进制
$addressStr = "0:312ed56825e7455a7948c34677e418e6c371d263c0fd7*****"; // 替换为实际地址
$payloadHex = "d0143e296879ed8f0000000064e328d8d751ea7c3c44827c8b6fcd5c83efcbfa"; // 替换为实际Payload十六进制
$domain = "ton-connect.github.io";
$timestamp = 1692608455;
$signatureHex = ""; // 替换为钱包返回的签名十六进制

// 1. 处理公钥:十六进制转二进制
$pubKey = hex2bin($pubKeyHex);

// 2. 处理地址:拆分workchain和hash并编码
list($workchainStr, $hashHex) = explode(':', $addressStr);
$workchain = (int)$workchainStr;
// 32位有符号大端序
$workchainBin = pack('N', $workchain);
// 256位无符号大端序(32字节)
$hashBin = hex2bin($hashHex);
$addressBin = $workchainBin . $hashBin;

// 3. 处理AppDomain
$domainUtf8 = utf8_encode($domain);
$domainLen = strlen($domainUtf8);
// 32位大端序长度
$domainLenBin = pack('N', $domainLen);
$appDomainBin = $domainLenBin . $domainUtf8;

// 4. 处理Timestamp:64位大端序
$timestampBin = pack('J', $timestamp); // J表示无符号64位大端序,PHP 7.2+支持

// 5. 处理Payload:十六进制转二进制
$payloadBin = hex2bin($payloadHex);

// 6. 组装原始message
$prefix = utf8_encode("ton-proof-item-v2/");
$message = $prefix . $addressBin . $appDomainBin . $timestampBin . $payloadBin;

// 7. 计算嵌套哈希
$messageHash = hash('sha256', $message, true);
$signPrefix = hex2bin('ffff') . utf8_encode("ton-connect");
$signHash = hash('sha256', $signPrefix . $messageHash, true);

// 8. 验证签名(将签名十六进制转二进制)
$signature = hex2bin($signatureHex);
$isValid = sodium_crypto_sign_verify_detached($signature, $signHash, $pubKey);

echo $isValid ? "验证通过" : "验证失败";
?>

注意事项

  • 确保PHP版本≥7.2,支持pack函数的J格式(64位大端序)
  • 所有十六进制输入(公钥、地址hash、payload、签名)必须是正确的十六进制字符串,不含空格或前缀
  • 若使用的是有符号时间戳,可将pack('J', $timestamp)改为pack('P', $timestamp)(有符号64位大端序)

内容的提问来源于stack exchange,提问作者Anton Bogomolov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 18:05:05