0

0

解析 v3 Onion 域名:提取公钥、校验和与版本信息

碧海醫心

碧海醫心

发布时间:2025-11-18 13:02:12

|

589人浏览过

|

来源于php中文网

原创

解析 v3 Onion 域名:提取公钥、校验和与版本信息

本教程详细介绍了如何解析 tor v3 onion 域名,以程序化方式提取其核心组成部分:公钥、校验和与版本号。我们将依据 tor 官方规范,通过 php 语言实现 base32 解码,并精确地从解码后的二进制数据中定位并提取这些关键信息。文章还将提供完整的示例代码和校验和验证方法,确保解析的准确性和可靠性。

引言:理解 v3 Onion 域名结构

Tor v3 Onion 域名是 Tor 隐藏服务在网络中的标识符,它们以 .onion 结尾,并提供了一种加密且匿名的访问方式。理解其内部结构对于进行安全分析、服务识别或构建相关工具至关重要。Tor 项目的官方规范 rend-spec-v3.txt(特别是 4.3.6 节)详细定义了 v3 Onion 域名的编码方式。

根据规范,一个 v3 Onion 域名由以下部分构成,并经过 Base32 编码:

onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"

其中:

  • PUBKEY: 隐藏服务的 32 字节 Ed25519 主公钥。
  • VERSION: 一个字节的版本字段,默认值为 \x03(即十进制 3)。
  • CHECKSUM: 一个 2 字节的校验和,其计算方式为 H(".onion checksum" | PUBKEY | VERSION)[:2],其中 H 表示 SHA3-256 哈希函数,".onion checksum" 是一个常量字符串,[:2] 表示取哈希结果的前两个字节。

这意味着,当我们对一个 v3 Onion 域名(去除 .onion 后缀)进行 Base32 解码后,会得到一个 35 字节的二进制字符串,其结构为:[32 字节公钥] [2 字节校验和] [1 字节版本号]。

解析流程概述

解析 v3 Onion 域名的主要步骤如下:

  1. 移除 .onion 后缀:从完整的 Onion 域名中去除末尾的 .onion 字符串。
  2. Base32 解码:对剩余的 Base32 编码字符串进行解码,得到原始的二进制数据。
  3. 提取组件:根据规范中定义的字节偏移量,从解码后的二进制数据中精确提取公钥、校验和和版本号。
  4. 校验和验证(可选但推荐):根据提取出的公钥和版本号,重新计算校验和,并与提取的校验和进行比对,以验证 Onion 域名的完整性和有效性。

PHP 实现:逐步解析 v3 Onion 域名

我们将使用 PHP 语言来演示如何实现上述解析过程。

SumiNote
SumiNote

一款服务留学生的AI学习神器

下载

步骤一:准备 Base32 解码器

PHP 标准库中没有内置 Base32 解码函数,因此我们需要一个自定义的实现。以下是一个简洁的 Base32 解码类:

<?php

class Base32
{
    private static $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';

    /**
     * 对 Base32 编码的字符串进行解码。
     * @param string $input Base32 编码字符串。
     * @return string 解码后的二进制字符串。
     */
    public static function decode(string $input): string
    {
        $input = strtoupper($input); // Base32 不区分大小写,通常转为大写处理
        $output = '';
        $buffer = 0;
        $bits = 0;

        for ($i = 0; $i < strlen($input); $i++) {
            $char = $input[$i];
            $val = strpos(self::$alphabet, $char);

            if ($val === false) {
                // 遇到无效字符,根据需求可以抛出异常或跳过
                // 对于 Tor Onion 域名,通常期望输入是有效的 Base32 字符串
                continue;
            }

            $buffer = ($buffer << 5) | $val; // 将当前 5 位值移入缓冲区
            $bits += 5; // 累计缓冲区中的位数

            // 当缓冲区中的位数达到或超过 8 位时,提取一个字节
            while ($bits >= 8) {
                $bits -= 8;
                $output .= chr(($buffer >> $bits) & 0xFF); // 提取最高 8 位作为字节
            }
        }
        return $output;
    }
}

?>

步骤二:移除后缀并解码

现在,我们可以使用上述 Base32 类来处理一个示例的 v3 Onion 域名。

<?php

// 假设 Base32 类已定义或包含在同一文件中
// require_once 'Base32.php';

$onionUrl = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion";

// 1. 移除 .onion 后缀
$base32EncodedString = str_replace(".onion", "", $onionUrl);

// 2. Base32 解码
$decodedBinaryData = Base32::decode($base32EncodedString);

// 验证解码后的长度是否符合预期 (32 + 2 + 1 = 35 字节)
if (strlen($decodedBinaryData) !== 35) {
    die("错误:解码后的数据长度不符合 v3 Onion 域名规范。\n");
}

echo "Base32 解码后的二进制数据 (Hex): " . bin2hex($decodedBinaryData) . "\n";

?>

步骤三:提取公钥、校验和与版本

根据 v3 Onion 域名的结构 [32 字节公钥] [2 字节校验和] [1 字节版本号],我们可以使用 substr 函数来精确提取这些组件。

<?php

// ... (接上一步骤的代码)

// 3. 提取公钥、校验和与版本
$publicKeyBytes = substr($decodedBinaryData, 0, 32); // 前 32 字节是公钥
$checksumBytes = substr($decodedBinaryData, 32, 2);  // 紧接着的 2 字节是校验和
$versionByte = substr($decodedBinaryData, 34, 1);    // 最后一个字节是版本号

echo "--------------------------------------------------\n";
echo "提取结果:\n";
echo "公钥 (32 字节 Hex): " . bin2hex($publicKeyBytes) . "\n";
echo "校验和 (2 字节 Hex): " . bin2hex($checksumBytes) . "\n";
echo "版本号 (1 字节 Hex): " . bin2hex($versionByte) . " (十进制: " . ord($versionByte) . ")\n";
echo "--------------------------------------------------\n";

?>

通过上述代码,我们成功地从一个 v3 Onion 域名中提取了其核心组成部分。

校验和验证

为了确保解析出的数据是有效的,并且 Onion 域名本身没有被篡改,我们可以重新计算校验和并与提取出的校验和进行比对。

根据规范:CHECKSUM = H(".onion checksum" | PUBKEY | VERSION)[:2]。这里 H 是 SHA3-256 哈希函数。PHP 7.1 及以上版本支持 SHA3 系列哈希算法。

<?php

// ... (接上一步骤的代码)

// 4. 校验和验证
$constantString = ".onion checksum";
$dataToHash = $constantString . $publicKeyBytes . $versionByte;

// 计算 SHA3-256 哈希
// 注意:PHP 7.1+ 才支持 'sha3-256'
if (in_array('sha3-256', hash_algos())) {
    $calculatedHash = hash('sha3-256', $dataToHash, true); // true 返回原始二进制数据
    $calculatedChecksum = substr($calculatedHash, 0, 2); // 取前两个字节作为校验和

    echo "重新计算的校验和 (2 字节 Hex): " . bin2hex($calculatedChecksum) . "\n";

    if ($calculatedChecksum === $checksumBytes) {
        echo "校验和验证成功:提取的校验和与计算结果一致。\n";
    } else {
        echo "校验和验证失败:提取的校验和与计算结果不一致!该 Onion 域名可能无效或已被篡改。\n";
    }
} else {
    echo "警告:当前 PHP 版本不支持 SHA3-256 哈希算法,无法进行校验和验证。\n";
}

?>

完整 PHP 示例代码

将上述所有步骤整合到一个完整的 PHP 脚本中:

<?php

class Base32
{
    private static $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';

    public static function decode(string $input): string
    {
        $input = strtoupper($input);
        $output = '';
        $buffer = 0;
        $bits = 0;

        for ($i = 0; $i < strlen($input); $i++) {
            $char = $input[$i];
            $val = strpos(self::$alphabet, $char);

            if ($val === false) {
                // 遇到无效字符,根据需求处理
                continue;
            }

            $buffer = ($buffer << 5) | $val;
            $bits += 5;

            while ($bits >= 8) {
                $bits -= 8;
                $output .= chr(($buffer >> $bits) & 0xFF);
            }
        }
        return $output;
    }
}

/**
 * 解析 v3 Onion 域名并提取其组件。
 * @param string $onionUrl 完整的 v3 Onion 域名。
 * @return array|null 包含 'publicKey', 'checksum', 'version' 的数组,或解析失败返回 null。
 */
function parseV3OnionDomain(string $onionUrl): ?array
{
    // 1. 移除 .onion 后缀
    if (!str_ends_with($onionUrl, ".onion")) {
        echo "错误:域名不以 '.onion' 结尾。\n";
        return null;
    }
    $base32EncodedString = str_replace(".onion", "", $onionUrl);

    // 2. Base32 解码
    $decodedBinaryData = Base32::decode($base32EncodedString);

    // 验证解码后的长度是否符合预期 (35 字节)
    if (strlen($decodedBinaryData) !== 35) {
        echo "错误:解码后的数据长度不符合 v3 Onion 域名规范 (预期 35 字节,实际 " . strlen($decodedBinaryData) . " 字节)。\n";
        return null;
    }

    // 3. 提取公钥、校验和与版本
    $publicKeyBytes = substr($decodedBinaryData, 0, 32); // 前 32 字节
    $checksumBytes = substr($decodedBinaryData, 32, 2);  // 紧接着的 2 字节
    $versionByte = substr($decodedBinaryData, 34, 1);    // 最后一个字节

    $result = [
        'publicKey' => bin2hex($publicKeyBytes),
        'checksum' => bin2hex($checksumBytes),
        'version' => ord($versionByte), // 将字节转换为十进制整数
        'rawPublicKey' => $publicKeyBytes,
        'rawChecksum' => $checksumBytes,
        'rawVersion' => $versionByte,
    ];

    // 4. 校验和验证
    if (in_array('sha3-256', hash_algos())) {
        $constantString = ".onion checksum";
        $dataToHash = $constantString . $publicKeyBytes . $versionByte;
        $calculatedHash = hash('sha3-256', $dataToHash, true);
        $calculatedChecksum = substr($calculatedHash, 0, 2);

        $result['checksumVerified'] = ($calculatedChecksum === $checksumBytes);
        $result['calculatedChecksum'] = bin2hex($calculatedChecksum);
    } else {
        $result['checksumVerified'] = false;
        $result['calculatedChecksum'] = 'N/A (SHA3-256 not supported)';
    }

    return $result;
}

// 示例使用
$testOnionUrl = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion";
$parsedData = parseV3OnionDomain($testOnionUrl);

if ($parsedData) {
    echo "--------------------------------------------------\n";
    echo "解析 v3 Onion 域名: " . $testOnionUrl . "\n";
    echo "--------------------------------------------------\n";
    echo "公钥 (Hex): " . $parsedData['publicKey'] . "\n";
    echo "提取的校验和 (Hex): " . $parsedData['checksum'] . "\n";
    echo "版本号 (十进制): " . $parsedData['version'] . "\n";
    echo "计算的校验和 (Hex): " . $parsedData['calculatedChecksum'] . "\n";
    echo "校验和验证结果: " . ($parsedData['checksumVerified'] ? "成功" : "失败") . "\n";
    echo "--------------------------------------------------\n";
}

// 另一个示例
$anotherOnionUrl = "sp3k262uwy4r2k3ycr5awluarykdpag6a7y33jxop4cs2lu5uz5sseqd.onion";
$parsedData2 = parseV3OnionDomain($anotherOnionUrl);
if ($parsedData2) {
    echo "\n--- 另一个示例 ---\n";
    echo "解析 v3 Onion 域名: " . $anotherOnionUrl . "\n";
    echo "公钥 (Hex): " . $parsedData2['publicKey'] . "\n";
    echo "提取的校验和 (Hex): " . $parsedData2['checksum'] . "\n";
    echo "版本号 (十进制): " . $parsedData2['version'] . "\n";
    echo "校验和验证结果: " . ($parsedData2['checksumVerified'] ? "成功" : "失败") . "\n";
}

?>

注意事项

  • Base32 解码器的可靠性:确保使用的 Base32 解码器实现是准确且符合 RFC 4648 规范的,尤其是在处理不同字符集和填充规则时。本教程提供的实现适用于 Tor v3 Onion 域名,因为它们通常不包含填充字符。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1561

2023.10.24

mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

206

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

313

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

290

2025.06.11

c++标识符介绍
c++标识符介绍

本专题整合了c++标识符相关内容,阅读专题下面的文章了解更多详细内容。

172

2025.08.07

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

678

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

219

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1561

2023.10.24

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

3

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 12.9万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号