I am trying to generate the onion address that is generated from a public key.
If the following line is added to the code in a previous post, just after privateKeyEncoded
String publicKeyEncoded = encoder.encodeToString(publicKey.getEncoded());
When I put the privateKeyEncoded into the /var/lib/tor/hidden_service/private_key, save the publicKeyEncoded and start the tor service, a new onion address is created. I am trying to get the same onion address as the tor service and from one created from the publicKeyEncoded. Using this code
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Base64;
//base64 string from the public key created
String publicKeyTest = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMnFkJTMZ2ZxnqLwCiB/EWHjsHbnC+sKEIrGbyOTYiTl3LygsekAX6LhgcllscLUFqSKlMRB3jRB0GAPrIc73E/hTnmWBtF8NT8DhZzl06LZ1BtNjfON1pHm87STMAayiSaXPmSOwIqOA89aJPcA9m4v4IhtjYSFXmCAsE4RqoAwIDAQAB";
//the onion address the tor service gives when the private key is used
String onionAddressTest = "qqkhrc4men3fiqyl";
byte[] publicKeyDecoded = Base64.decodeBase64(publicKeyTest);
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
byte[] sha1hash = messageDigest.digest(publicKeyDecoded);
int numberOfCharacters = 10;
byte[] reducedHash = new byte[numberOfCharacters];
for(int i = 0; i < numberOfCharacters; i++) {
reducedHash[i] = sha1hash[i];
}
Base32 base32encoder = new Base32();
String onionAddress = base32encoder.encodeAsString(reducedHash).toLowerCase();
System.out.println(onionAddress); // but this gives "7j3iz4of464hje2e"
I've tried using spongycastle to replicate my conversion but get the same answer. Which makes me think there's something wrong with how I generate the public key or there's something wrong in my initial conversion from base64.
Given the public key (publicKeyTest) how can you get the onion address (onionAddressTest) using java?
According to this and this you need to hash only the part starting at offset 22 of the X.509 SubjectPublicKeyInfo encoding used by Java which calls it 'X.509' and by OpenSSL which calls it 'PUBKEY'. I can't find any actual Tor doc on this, but I don't believe it can be accidental that this is exactly the beginning of the algorithm-dependent data in SPKI format for an RSA-1024 key:
$ openssl asn1parse -i <49833260.b64
0:d=0 hl=3 l= 159 cons: SEQUENCE
3:d=1 hl=2 l= 13 cons: SEQUENCE
5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
16:d=2 hl=2 l= 0 prim: NULL
18:d=1 hl=3 l= 141 prim: BIT STRING
# 18 +3 for DER tag+len +1 for unusedbitcount in BITSTRING = 22
# and the content beginning at 22 is:
$ openssl asn1parse -i -strparse 22 <49833260.b64
0:d=0 hl=3 l= 137 cons: SEQUENCE
3:d=1 hl=3 l= 129 prim: INTEGER :8C9C59094CC6766719EA2F00A207F11
61E3B076E70BEB0A108AC66F23936224E5DCBCA0B1E9005FA2E181C965B1C2D416A48A94C441DE34
41D0600FAC873BDC4FE14E799606D17C353F03859CE5D3A2D9D41B4D8DF38DD691E6F3B4933006B2
8926973E648EC08A8E03CF5A24F700F66E2FE0886D8D84855E6080B04E11AA803
135:d=1 hl=2 l= 3 prim: INTEGER :010001
# which is (exactly) the RSAPublicKey structure from PKCS1
So to do this in Java you can just assume RSA-1024, or with BouncyCastle (and I assume, but haven't tested, spongycastle as well) you can actually parse the ASN.1 properly:
byte[] pubkeyder = Base64.decode("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMnFkJTMZ2ZxnqLwCiB/EWHjsHbnC+sKEIrGbyOTYiTl3LygsekAX6LhgcllscLUFqSKlMRB3jRB0GAPrIc73E/hTnmWBtF8NT8DhZzl06LZ1BtNjfON1pHm87STMAayiSaXPmSOwIqOA89aJPcA9m4v4IhtjYSFXmCAsE4RqoAwIDAQAB");
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
// method 1
byte[] x1 = sha1.digest (Arrays.copyOfRange(pubkeyder, 22, pubkeyder.length));
System.out.println (new String(b32enc(Arrays.copyOf(x1,10))).toLowerCase());
// method 2
byte[] x2 = sha1.digest (SubjectPublicKeyInfo.getInstance(pubkeyder).getPublicKeyData().getOctets());
System.out.println (new String(b32enc(Arrays.copyOf(x2,10))).toLowerCase());
->
qqkhrc4men3fiqyl
qqkhrc4men3fiqyl