3

I am trying to generate a public and private key set from a input string in Java. (Yes I am aware this can be a dangerous practice) I am using bitcoinj for a outside library.

I currently have:

 //public key generation from private key
 static String getPublicKey(byte[] privKey) {

    Address address = new Address(MainNetParams.get(), 
        Utils.sha256hash160(ECKey.fromPrivate(privKey, false).getPubKey()));

    return address.toString();

  }

 ///hash string to generate private key from string
 static byte[] sha256(String base) {
      try{
          MessageDigest digest = MessageDigest.getInstance("SHA-256");
          byte[] hash = digest.digest(base.getBytes("UTF-8"));
        return hash;
      } catch(Exception ex){
      throw new RuntimeException(ex);
      }
    }

 //encode private key as string to display
 static String privToString(byte[] hash) {

      StringBuffer hexString = new StringBuffer();

      for (int i = 0; i < hash.length; i++) {
          String hex = Integer.toHexString(0xff & hash[i]);
          if(hex.length() == 1) hexString.append('0');
          hexString.append(hex);
      }

      return hexString.toString();

  }

When I run the following: Seed String: icecreampaintjob

I get the following:

Public: 1KdoiXMYFn2qa8uGGiNqfrwFRDu3j2qQNA

Private: dba1e3e22415c56af772dee422add21b7382ea35f2af77852a8069d02e47ecf4

Using bitaddress.org to cross verify I get:

Private: 5KV1o7tRK8pNqrPNYyi38nrik9r2Y85sjdgFDttnDiT1uZrQ1fj (DOESN'T MACTH)

Public: 1KdoiXMYFn2qa8uGGiNqfrwFRDu3j2qQNA (MATCHES)

What am I missing?

2 Answers 2

1

As bitaddress.org BrainWallet says in its display

Private Key (Wallet Import Format): 
5KV1o7tRK8pNqrPNYyi38nrik9r2Y85sjdgFDttnDiT1uZrQ1fj 

Wallet Import Format is the usual base58 armoring applied to the privatekey value with 'version' 0x80.
See https://en.bitcoin.it/wiki/Wallet_import_format or in more detail https://en.bitcoin.it/wiki/Base58Check_encoding . This is basically a duplicate of Is there a way to generate a brain wallet from the command line or console? although that uses bash+dc rather than Java.

Not tested, but it looks to me like VersionChecksummedBytes(0x80,bytes).toBase58() does this.

5
  • Where is VersionChecksummedBytes? Is the method part of the bitcoinj library?
    – John Down
    Apr 24, 2016 at 5:45
  • @JohnDown yes it's in bitcoinj. It's a class (in Java by convention class names start with uppercase letter and method names start with lowercase letter) and is the superclass of the Address class your code already uses -- that's how I found it. Apr 25, 2016 at 3:12
  • Can you further explain? The method is protected and I can't access it from my main class. Right now I have: Address output = new VersionChecksummedBytes(0x80,privateKeyByte).toBase58();
    – John Down
    Apr 25, 2016 at 16:58
  • See Java docs: bitcoinj.github.io/javadoc/0.12.3/…
    – John Down
    Apr 25, 2016 at 21:11
  • In the code (where I looked) actually the ctor is protected (and I didn't notice) but the method is public. (1) It's opensource so you could change it. (2) It's Java so you could override with reflection. (3) But the cleanest way is just make a very simple subclass, and use that. And (4) when you do, the result from toBase58 (or toString which just wraps it) is String. Apr 26, 2016 at 9:32
0

I think new VersionedChecksummedBytes(0x80, priv).toBase58() is the right idea, but the constructors are protected and I cannot find a static factory function. However, using the getPrivateKeyEncoded method of ECKey, we can obtain a DumpedPrivateKey object which derives from VersionedChecksummedBytes. We can also use the getPrivateKeyAsWiF method of ECKey which is more direct. Or we can do things manually:

import java.math.BigInteger;
import org.bitcoinj.core.VersionedChecksummedBytes;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Base58;
import org.bitcoinj.params.MainNetParams;



public class Test {
  public static void main(String args[]){

  NetworkParameters mainNet = MainNetParams.get();
  String hex = "dba1e3e22415c56af772dee422add21b7382ea35f2af77852a8069d02e47ecf4";
  BigInteger big = new BigInteger(hex, 16); 
  ECKey key = ECKey.fromPrivate(big, false);  // uncompressed
  byte[] priv = key.getPrivKeyBytes();

  String wif1 = key.getPrivateKeyEncoded(mainNet).toBase58();
  System.out.println(wif1); // 5KV1o7tRK8pNqrPNYyi38nrik9r2Y85sjdgFDttnDiT1uZrQ1fj

  String wif2 = key.getPrivateKeyAsWiF(mainNet);
  System.out.println(wif2); // 5KV1o7tRK8pNqrPNYyi38nrik9r2Y85sjdgFDttnDiT1uZrQ1fj

  byte[] bytes = new byte[1 + 32 + 4];
  bytes[0] = (byte) 0x80;
  System.arraycopy(priv, 0, bytes, 1, 32);
  byte[] checksum = Sha256Hash.hashTwice(bytes, 0, 33);
  System.arraycopy(checksum, 0, bytes, 33, 4);
  String wif3 = Base58.encode(bytes);
  System.out.println(wif3); // 5KV1o7tRK8pNqrPNYyi38nrik9r2Y85sjdgFDttnDiT1uZrQ1fj

  }
}

Beware that if you intend to do the same manual calculation for a compressed key, you need to add an additional byte 0x01 after the 32 bytes secret and just before the checksum. If you want to do the same thing for a testing network, replace the front version byte 0x80 by 0xef.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.