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

如何在Java中从公钥生成与Tor服务一致的洋葱地址?

Generating a Tor Onion Address in Java Matching Tor Service Output

I’ve worked through how Tor generates onion addresses from public keys, so let’s walk through exactly how to replicate that in Java to get the same address as your Tor service produces.

Key Background

Tor generates onion addresses by hashing raw public key material and encoding it in a specific Base32 format. The process differs slightly between legacy v2 (RSA) and modern v3 (Ed25519) hidden services, so we’ll cover both cases here.

Step-by-Step Implementation

Here’s a complete Java solution that matches Tor’s internal logic for both address types:

First, you’ll need the Apache Commons Codec library for proper Base32 encoding (Tor uses a lowercase, no-padding variant that Java’s built-in libraries don’t support natively).

1. Add Dependency (Maven/Gradle)

For Maven, add this to your pom.xml:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.15</version>
</dependency>

For Gradle:

implementation 'commons-codec:commons-codec:1.15'

2. Java Code to Generate Onion Address

import org.apache.commons.codec.binary.Base32;
import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.interfaces.EdECPublicKey;

public class OnionAddressGenerator {
    public static String generateOnionAddress(String publicKeyEncoded) throws Exception {
        // Decode the Base64-encoded public key string back to bytes
        byte[] publicKeyBytes = java.util.Base64.getDecoder().decode(publicKeyEncoded);
        
        // Parse the X.509 encoded public key into a usable PublicKey object
        KeyFactory keyFactory;
        try {
            keyFactory = KeyFactory.getInstance("Ed25519");
        } catch (Exception e) {
            // Fall back to RSA if Ed25519 isn't supported (older JDKs)
            keyFactory = KeyFactory.getInstance("RSA");
        }
        PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
        
        byte[] rawPublicKey;
        boolean isV3 = false;

        // Extract raw public key material based on key type
        if (publicKey instanceof EdECPublicKey) {
            isV3 = true;
            // For v3 addresses: get the raw 32-byte Ed25519 public key
            rawPublicKey = ((EdECPublicKey) publicKey).getEdECPoint().getEncoded(false);
        } else if (publicKey instanceof RSAPublicKey) {
            // For v2 addresses: get the RSA modulus (strip leading zero if present)
            rawPublicKey = ((RSAPublicKey) publicKey).getModulus().toByteArray();
            if (rawPublicKey[0] == 0) {
                byte[] temp = new byte[rawPublicKey.length - 1];
                System.arraycopy(rawPublicKey, 1, temp, 0, temp.length);
                rawPublicKey = temp;
            }
        } else {
            throw new IllegalArgumentException("Unsupported public key type (only RSA/Ed25519 are supported)");
        }

        // Compute the required hash
        MessageDigest digest = isV3 ? MessageDigest.getInstance("SHA-256") : MessageDigest.getInstance("SHA-1");
        byte[] hash = digest.digest(rawPublicKey);

        Base32 base32 = new Base32();
        String onionAddress;

        if (isV3) {
            // Build v3 address components: version byte + hash + checksum
            byte[] versionHash = new byte[33];
            versionHash[0] = 0x03; // v3 version marker
            System.arraycopy(hash, 0, versionHash, 1, 32);

            // Calculate checksum: SHA-256(version + hash + ".onion checksum")
            MessageDigest checksumDigest = MessageDigest.getInstance("SHA-256");
            checksumDigest.update(versionHash);
            checksumDigest.update(".onion checksum".getBytes());
            byte[] checksum = checksumDigest.digest();

            // Combine all components and encode
            byte[] fullAddressBytes = new byte[35];
            System.arraycopy(versionHash, 0, fullAddressBytes, 0, 33);
            System.arraycopy(checksum, 0, fullAddressBytes, 33, 2);
            onionAddress = base32.encodeToString(fullAddressBytes).toLowerCase() + ".onion";
        } else {
            // For v2: take first 10 bytes of SHA-1 hash, encode to Base32 (remove padding)
            byte[] addressBytes = new byte[10];
            System.arraycopy(hash, 0, addressBytes, 0, 10);
            onionAddress = base32.encodeToString(addressBytes).toLowerCase().replace("=", "") + ".onion";
        }

        return onionAddress;
    }

    public static void main(String[] args) throws Exception {
        // Replace this with your saved publicKeyEncoded string from earlier
        String yourPublicKeyEncoded = "INSERT_YOUR_BASE64_PUBLIC_KEY_HERE";
        String generatedOnion = generateOnionAddress(yourPublicKeyEncoded);
        System.out.println("Matching Onion Address: " + generatedOnion);
    }
}

How It Works

  • Decoding the Public Key: We first convert your saved publicKeyEncoded string back to a byte array, then parse it into a PublicKey object using the X.509 spec (which is what publicKey.getEncoded() returns).
  • Raw Key Extraction: For Ed25519 (v3), we grab the raw 32-byte public key. For RSA (v2), we extract the modulus and strip any leading zero added by BigInteger.toByteArray().
  • Hashing: v2 uses SHA-1 on the RSA modulus, v3 uses SHA-256 on the Ed25519 public key.
  • Base32 Encoding: Tor uses a lowercase, padding-free Base32 variant. For v3, we also add a version marker and checksum as specified in Tor’s v3 address format.

Testing It Out

Just replace INSERT_YOUR_BASE64_PUBLIC_KEY_HERE with the publicKeyEncoded string you saved earlier, run the code, and it should output exactly the same onion address as your Tor service generates when you use the corresponding private_key file.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:53:23