Commit 756b0751 authored by Andreas Schildbach's avatar Andreas Schildbach Committed by bitcoinj-sv

Option to decrypt private keys and seed on the fly if printing a wallet dump...

Option to decrypt private keys and seed on the fly if printing a wallet dump of an encrypted wallet.
parent f2ce027f
......@@ -1227,15 +1227,15 @@ public class ECKey implements EncryptableItem {
@Override
public String toString() {
return toString(false, null);
return toString(false, null, null);
}
/**
* Produce a string rendering of the ECKey INCLUDING the private key.
* Unless you absolutely need the private key it is better for security reasons to just use {@link #toString()}.
*/
public String toStringWithPrivate(NetworkParameters params) {
return toString(true, params);
public String toStringWithPrivate(@Nullable KeyParameter aesKey, NetworkParameters params) {
return toString(true, aesKey, params);
}
public String getPrivateKeyAsHex() {
......@@ -1250,13 +1250,14 @@ public class ECKey implements EncryptableItem {
return getPrivateKeyEncoded(params).toString();
}
private String toString(boolean includePrivate, NetworkParameters params) {
private String toString(boolean includePrivate, @Nullable KeyParameter aesKey, NetworkParameters params) {
final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this).omitNullValues();
helper.add("pub HEX", getPublicKeyAsHex());
if (includePrivate) {
ECKey decryptedKey = isEncrypted() ? decrypt(checkNotNull(aesKey)) : this;
try {
helper.add("priv HEX", getPrivateKeyAsHex());
helper.add("priv WIF", getPrivateKeyAsWiF(params));
helper.add("priv HEX", decryptedKey.getPrivateKeyAsHex());
helper.add("priv WIF", decryptedKey.getPrivateKeyAsWiF(params));
} catch (IllegalStateException e) {
// TODO: Make hasPrivKey() work for deterministic keys and fix this.
} catch (Exception e) {
......@@ -1274,7 +1275,8 @@ public class ECKey implements EncryptableItem {
return helper.toString();
}
public void formatKeyWithAddress(boolean includePrivateKeys, StringBuilder builder, NetworkParameters params) {
public void formatKeyWithAddress(boolean includePrivateKeys, @Nullable KeyParameter aesKey, StringBuilder builder,
NetworkParameters params) {
final Address address = toAddress(params);
builder.append(" addr:");
builder.append(address.toString());
......@@ -1285,7 +1287,7 @@ public class ECKey implements EncryptableItem {
builder.append("\n");
if (includePrivateKeys) {
builder.append(" ");
builder.append(toStringWithPrivate(params));
builder.append(toStringWithPrivate(aesKey, params));
builder.append("\n");
}
}
......
......@@ -614,13 +614,14 @@ public class DeterministicKey extends ECKey {
}
@Override
public void formatKeyWithAddress(boolean includePrivateKeys, StringBuilder builder, NetworkParameters params) {
public void formatKeyWithAddress(boolean includePrivateKeys, @Nullable KeyParameter aesKey, StringBuilder builder,
NetworkParameters params) {
final Address address = toAddress(params);
builder.append(" addr:").append(address);
builder.append(" hash160:").append(Utils.HEX.encode(getPubKeyHash()));
builder.append(" (").append(getPathAsString()).append(")\n");
if (includePrivateKeys) {
builder.append(" ").append(toStringWithPrivate(params)).append("\n");
builder.append(" ").append(toStringWithPrivate(aesKey, params)).append("\n");
}
}
}
......@@ -802,16 +802,16 @@ public class KeyChainGroup implements KeyBag {
}
}
public String toString(boolean includePrivateKeys) {
public String toString(boolean includePrivateKeys, @Nullable KeyParameter aesKey) {
final StringBuilder builder = new StringBuilder();
if (basic != null) {
List<ECKey> keys = basic.getKeys();
Collections.sort(keys, ECKey.AGE_COMPARATOR);
for (ECKey key : keys)
key.formatKeyWithAddress(includePrivateKeys, builder, params);
key.formatKeyWithAddress(includePrivateKeys, aesKey, builder, params);
}
for (DeterministicKeyChain chain : chains)
builder.append(chain.toString(includePrivateKeys, params)).append('\n');
builder.append(chain.toString(includePrivateKeys, aesKey, params)).append('\n');
return builder.toString();
}
......
......@@ -28,6 +28,7 @@ import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.spongycastle.crypto.params.KeyParameter;
import java.security.SecureRandom;
import java.util.LinkedHashMap;
......@@ -234,7 +235,8 @@ public class MarriedKeyChain extends DeterministicKeyChain {
}
@Override
protected void formatAddresses(boolean includePrivateKeys, NetworkParameters params, StringBuilder builder2) {
protected void formatAddresses(boolean includePrivateKeys, @Nullable KeyParameter aesKey, NetworkParameters params,
StringBuilder builder2) {
for (DeterministicKeyChain followingChain : followingKeyChains)
builder2.append("Following chain: ").append(followingChain.getWatchingKey().serializePubB58(params))
.append('\n');
......
......@@ -3196,20 +3196,29 @@ public class Wallet extends BaseTaggableObject
@Override
public String toString() {
return toString(false, true, true, null);
return toString(false, null, true, true, null);
}
/**
* @deprecated Use {@link #toString(boolean, KeyParameter, boolean, boolean, AbstractBlockChain)} instead.
*/
@Deprecated
public String toString(boolean includePrivateKeys, boolean includeTransactions, boolean includeExtensions,
@Nullable AbstractBlockChain chain) {
return toString(includePrivateKeys, includeTransactions, includeExtensions, chain);
}
/**
* Formats the wallet as a human readable piece of text. Intended for debugging, the format is not meant to be
* stable or human readable.
* @param includePrivateKeys Whether raw private key data should be included.
* @param key for decrypting private key data for if the wallet is encrypted.
* @param includeTransactions Whether to print transaction data.
* @param includeExtensions Whether to print extension data.
* @param chain If set, will be used to estimate lock times for block timelocked transactions.
*/
public String toString(boolean includePrivateKeys, boolean includeTransactions, boolean includeExtensions,
@Nullable AbstractBlockChain chain) {
public String toString(boolean includePrivateKeys, @Nullable KeyParameter aesKey, boolean includeTransactions,
boolean includeExtensions, @Nullable AbstractBlockChain chain) {
lock.lock();
keyChainGroupLock.lock();
try {
......@@ -3239,7 +3248,7 @@ public class Wallet extends BaseTaggableObject
final Date keyRotationTime = getKeyRotationTime();
if (keyRotationTime != null)
builder.append("Key rotation time: ").append(Utils.dateTimeFormat(keyRotationTime)).append('\n');
builder.append(keyChainGroup.toString(includePrivateKeys));
builder.append(keyChainGroup.toString(includePrivateKeys, aesKey));
if (!watchedScripts.isEmpty()) {
builder.append("\nWatched scripts:\n");
......
......@@ -317,7 +317,7 @@ public class ECKeyTest {
ECKey key = ECKey.fromPrivate(BigInteger.TEN).decompress(); // An example private key.
NetworkParameters params = MainNetParams.get();
assertEquals("ECKey{pub HEX=04a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7, isEncrypted=false, isPubKeyOnly=false}", key.toString());
assertEquals("ECKey{pub HEX=04a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7, priv HEX=000000000000000000000000000000000000000000000000000000000000000a, priv WIF=5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreBoNWTw6, isEncrypted=false, isPubKeyOnly=false}", key.toStringWithPrivate(params));
assertEquals("ECKey{pub HEX=04a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7, priv HEX=000000000000000000000000000000000000000000000000000000000000000a, priv WIF=5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreBoNWTw6, isEncrypted=false, isPubKeyOnly=false}", key.toStringWithPrivate(null, params));
}
@Test
......
......@@ -1459,7 +1459,21 @@ public class WalletTool {
// there just for the dump case.
if (chainFileName.exists())
setup();
System.out.println(wallet.toString(options.has("dump-privkeys"), true, true, chain));
final boolean dumpPrivkeys = options.has("dump-privkeys");
if (dumpPrivkeys && wallet.isEncrypted()) {
if (password != null) {
final KeyParameter aesKey = passwordToKey(true);
if (aesKey == null)
return; // Error message already printed.
System.out.println(wallet.toString(true, aesKey, true, true, chain));
} else {
System.err.println("Can't dump privkeys, wallet is encrypted.");
return;
}
} else {
System.out.println(wallet.toString(dumpPrivkeys, null, true, true, chain));
}
}
private static void setCreationTime() {
......
......@@ -4,8 +4,9 @@ Usage: wallet-tool --flags action-name
wallet-tool action-name --flags
>>> ACTIONS
dump Loads and prints the given wallet in textual form to stdout. Private keys are only printed
if --dump-privkeys is specified.
dump Loads and prints the given wallet in textual form to stdout. Private keys and seed are only
printed if --dump-privkeys is specified. If the wallet is encrypted, also specify the --password
option to dump the private keys and seed.
raw-dump Prints the wallet as a raw protobuf with no parsing or sanity checking applied.
create Makes a new wallet in the file specified by --wallet.
Will complain and require --force if the wallet already exists.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment