mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-01-18 21:32:35 +01:00
Option to decrypt private keys and seed on the fly if printing a wallet dump of an encrypted wallet.
This commit is contained in:
parent
49e6af3bd7
commit
ca033e3368
@ -1224,15 +1224,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() {
|
||||
@ -1247,13 +1247,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) {
|
||||
@ -1271,7 +1272,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());
|
||||
@ -1282,7 +1284,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");
|
||||
}
|
||||
}
|
||||
|
@ -615,13 +615,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1311,16 +1311,19 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public String toString(boolean includePrivateKeys, NetworkParameters params) {
|
||||
public String toString(boolean includePrivateKeys, @Nullable KeyParameter aesKey, NetworkParameters params) {
|
||||
final DeterministicKey watchingKey = getWatchingKey();
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
if (seed != null) {
|
||||
if (seed.isEncrypted()) {
|
||||
builder.append("Seed is encrypted\n");
|
||||
} else if (includePrivateKeys) {
|
||||
final List<String> words = seed.getMnemonicCode();
|
||||
if (includePrivateKeys) {
|
||||
DeterministicSeed decryptedSeed = seed.isEncrypted()
|
||||
? seed.decrypt(getKeyCrypter(), DEFAULT_PASSPHRASE_FOR_MNEMONIC, aesKey) : seed;
|
||||
final List<String> words = decryptedSeed.getMnemonicCode();
|
||||
builder.append("Seed as words: ").append(Utils.SPACE_JOINER.join(words)).append('\n');
|
||||
builder.append("Seed as hex: ").append(seed.toHexString()).append('\n');
|
||||
builder.append("Seed as hex: ").append(decryptedSeed.toHexString()).append('\n');
|
||||
} else {
|
||||
if (seed.isEncrypted())
|
||||
builder.append("Seed is encrypted\n");
|
||||
}
|
||||
builder.append("Seed birthday: ").append(seed.getCreationTimeSeconds()).append(" [")
|
||||
.append(Utils.dateTimeFormat(seed.getCreationTimeSeconds() * 1000)).append("]\n");
|
||||
@ -1329,13 +1332,14 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||
.append(Utils.dateTimeFormat(watchingKey.getCreationTimeSeconds() * 1000)).append("]\n");
|
||||
}
|
||||
builder.append("Key to watch: ").append(watchingKey.serializePubB58(params)).append('\n');
|
||||
formatAddresses(includePrivateKeys, params, builder);
|
||||
formatAddresses(includePrivateKeys, aesKey, params, builder);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected void formatAddresses(boolean includePrivateKeys, NetworkParameters params, StringBuilder builder) {
|
||||
protected void formatAddresses(boolean includePrivateKeys, @Nullable KeyParameter aesKey, NetworkParameters params,
|
||||
StringBuilder builder) {
|
||||
for (ECKey key : getKeys(false, true))
|
||||
key.formatKeyWithAddress(includePrivateKeys, builder, params);
|
||||
key.formatKeyWithAddress(includePrivateKeys, aesKey, builder, params);
|
||||
}
|
||||
|
||||
/** The number of signatures required to spend coins received by this keychain. */
|
||||
|
@ -785,16 +785,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');
|
||||
|
@ -3183,20 +3183,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 {
|
||||
@ -3226,7 +3235,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
|
||||
|
@ -1447,7 +1447,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.
|
||||
|
Loading…
Reference in New Issue
Block a user