mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-15 11:45:51 +00: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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return toString(false, null);
|
return toString(false, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a string rendering of the ECKey INCLUDING the private key.
|
* 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()}.
|
* Unless you absolutely need the private key it is better for security reasons to just use {@link #toString()}.
|
||||||
*/
|
*/
|
||||||
public String toStringWithPrivate(NetworkParameters params) {
|
public String toStringWithPrivate(@Nullable KeyParameter aesKey, NetworkParameters params) {
|
||||||
return toString(true, params);
|
return toString(true, aesKey, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPrivateKeyAsHex() {
|
public String getPrivateKeyAsHex() {
|
||||||
@ -1247,13 +1247,14 @@ public class ECKey implements EncryptableItem {
|
|||||||
return getPrivateKeyEncoded(params).toString();
|
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();
|
final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this).omitNullValues();
|
||||||
helper.add("pub HEX", getPublicKeyAsHex());
|
helper.add("pub HEX", getPublicKeyAsHex());
|
||||||
if (includePrivate) {
|
if (includePrivate) {
|
||||||
|
ECKey decryptedKey = isEncrypted() ? decrypt(checkNotNull(aesKey)) : this;
|
||||||
try {
|
try {
|
||||||
helper.add("priv HEX", getPrivateKeyAsHex());
|
helper.add("priv HEX", decryptedKey.getPrivateKeyAsHex());
|
||||||
helper.add("priv WIF", getPrivateKeyAsWiF(params));
|
helper.add("priv WIF", decryptedKey.getPrivateKeyAsWiF(params));
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
// TODO: Make hasPrivKey() work for deterministic keys and fix this.
|
// TODO: Make hasPrivKey() work for deterministic keys and fix this.
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -1271,7 +1272,8 @@ public class ECKey implements EncryptableItem {
|
|||||||
return helper.toString();
|
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);
|
final Address address = toAddress(params);
|
||||||
builder.append(" addr:");
|
builder.append(" addr:");
|
||||||
builder.append(address.toString());
|
builder.append(address.toString());
|
||||||
@ -1282,7 +1284,7 @@ public class ECKey implements EncryptableItem {
|
|||||||
builder.append("\n");
|
builder.append("\n");
|
||||||
if (includePrivateKeys) {
|
if (includePrivateKeys) {
|
||||||
builder.append(" ");
|
builder.append(" ");
|
||||||
builder.append(toStringWithPrivate(params));
|
builder.append(toStringWithPrivate(aesKey, params));
|
||||||
builder.append("\n");
|
builder.append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -615,13 +615,14 @@ public class DeterministicKey extends ECKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
final Address address = toAddress(params);
|
||||||
builder.append(" addr:").append(address);
|
builder.append(" addr:").append(address);
|
||||||
builder.append(" hash160:").append(Utils.HEX.encode(getPubKeyHash()));
|
builder.append(" hash160:").append(Utils.HEX.encode(getPubKeyHash()));
|
||||||
builder.append(" (").append(getPathAsString()).append(")\n");
|
builder.append(" (").append(getPathAsString()).append(")\n");
|
||||||
if (includePrivateKeys) {
|
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();
|
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 DeterministicKey watchingKey = getWatchingKey();
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
if (seed != null) {
|
if (seed != null) {
|
||||||
if (seed.isEncrypted()) {
|
if (includePrivateKeys) {
|
||||||
builder.append("Seed is encrypted\n");
|
DeterministicSeed decryptedSeed = seed.isEncrypted()
|
||||||
} else if (includePrivateKeys) {
|
? seed.decrypt(getKeyCrypter(), DEFAULT_PASSPHRASE_FOR_MNEMONIC, aesKey) : seed;
|
||||||
final List<String> words = seed.getMnemonicCode();
|
final List<String> words = decryptedSeed.getMnemonicCode();
|
||||||
builder.append("Seed as words: ").append(Utils.SPACE_JOINER.join(words)).append('\n');
|
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(" [")
|
builder.append("Seed birthday: ").append(seed.getCreationTimeSeconds()).append(" [")
|
||||||
.append(Utils.dateTimeFormat(seed.getCreationTimeSeconds() * 1000)).append("]\n");
|
.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");
|
.append(Utils.dateTimeFormat(watchingKey.getCreationTimeSeconds() * 1000)).append("]\n");
|
||||||
}
|
}
|
||||||
builder.append("Key to watch: ").append(watchingKey.serializePubB58(params)).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();
|
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))
|
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. */
|
/** 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();
|
final StringBuilder builder = new StringBuilder();
|
||||||
if (basic != null) {
|
if (basic != null) {
|
||||||
List<ECKey> keys = basic.getKeys();
|
List<ECKey> keys = basic.getKeys();
|
||||||
Collections.sort(keys, ECKey.AGE_COMPARATOR);
|
Collections.sort(keys, ECKey.AGE_COMPARATOR);
|
||||||
for (ECKey key : keys)
|
for (ECKey key : keys)
|
||||||
key.formatKeyWithAddress(includePrivateKeys, builder, params);
|
key.formatKeyWithAddress(includePrivateKeys, aesKey, builder, params);
|
||||||
}
|
}
|
||||||
for (DeterministicKeyChain chain : chains)
|
for (DeterministicKeyChain chain : chains)
|
||||||
builder.append(chain.toString(includePrivateKeys, params)).append('\n');
|
builder.append(chain.toString(includePrivateKeys, aesKey, params)).append('\n');
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import org.bitcoinj.crypto.DeterministicKey;
|
|||||||
import org.bitcoinj.crypto.KeyCrypter;
|
import org.bitcoinj.crypto.KeyCrypter;
|
||||||
import org.bitcoinj.script.Script;
|
import org.bitcoinj.script.Script;
|
||||||
import org.bitcoinj.script.ScriptBuilder;
|
import org.bitcoinj.script.ScriptBuilder;
|
||||||
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -234,7 +235,8 @@ public class MarriedKeyChain extends DeterministicKeyChain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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)
|
for (DeterministicKeyChain followingChain : followingKeyChains)
|
||||||
builder2.append("Following chain: ").append(followingChain.getWatchingKey().serializePubB58(params))
|
builder2.append("Following chain: ").append(followingChain.getWatchingKey().serializePubB58(params))
|
||||||
.append('\n');
|
.append('\n');
|
||||||
|
@ -3183,20 +3183,29 @@ public class Wallet extends BaseTaggableObject
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
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
|
* Formats the wallet as a human readable piece of text. Intended for debugging, the format is not meant to be
|
||||||
* stable or human readable.
|
* stable or human readable.
|
||||||
* @param includePrivateKeys Whether raw private key data should be included.
|
* @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 includeTransactions Whether to print transaction data.
|
||||||
* @param includeExtensions Whether to print extension data.
|
* @param includeExtensions Whether to print extension data.
|
||||||
* @param chain If set, will be used to estimate lock times for block timelocked transactions.
|
* @param chain If set, will be used to estimate lock times for block timelocked transactions.
|
||||||
*/
|
*/
|
||||||
public String toString(boolean includePrivateKeys, boolean includeTransactions, boolean includeExtensions,
|
public String toString(boolean includePrivateKeys, @Nullable KeyParameter aesKey, boolean includeTransactions,
|
||||||
@Nullable AbstractBlockChain chain) {
|
boolean includeExtensions, @Nullable AbstractBlockChain chain) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
keyChainGroupLock.lock();
|
keyChainGroupLock.lock();
|
||||||
try {
|
try {
|
||||||
@ -3226,7 +3235,7 @@ public class Wallet extends BaseTaggableObject
|
|||||||
final Date keyRotationTime = getKeyRotationTime();
|
final Date keyRotationTime = getKeyRotationTime();
|
||||||
if (keyRotationTime != null)
|
if (keyRotationTime != null)
|
||||||
builder.append("Key rotation time: ").append(Utils.dateTimeFormat(keyRotationTime)).append('\n');
|
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()) {
|
if (!watchedScripts.isEmpty()) {
|
||||||
builder.append("\nWatched scripts:\n");
|
builder.append("\nWatched scripts:\n");
|
||||||
|
@ -317,7 +317,7 @@ public class ECKeyTest {
|
|||||||
ECKey key = ECKey.fromPrivate(BigInteger.TEN).decompress(); // An example private key.
|
ECKey key = ECKey.fromPrivate(BigInteger.TEN).decompress(); // An example private key.
|
||||||
NetworkParameters params = MainNetParams.get();
|
NetworkParameters params = MainNetParams.get();
|
||||||
assertEquals("ECKey{pub HEX=04a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7, isEncrypted=false, isPubKeyOnly=false}", key.toString());
|
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
|
@Test
|
||||||
|
@ -1447,7 +1447,21 @@ public class WalletTool {
|
|||||||
// there just for the dump case.
|
// there just for the dump case.
|
||||||
if (chainFileName.exists())
|
if (chainFileName.exists())
|
||||||
setup();
|
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() {
|
private static void setCreationTime() {
|
||||||
|
@ -4,8 +4,9 @@ Usage: wallet-tool --flags action-name
|
|||||||
wallet-tool action-name --flags
|
wallet-tool action-name --flags
|
||||||
|
|
||||||
>>> ACTIONS
|
>>> ACTIONS
|
||||||
dump Loads and prints the given wallet in textual form to stdout. Private keys are only printed
|
dump Loads and prints the given wallet in textual form to stdout. Private keys and seed are only
|
||||||
if --dump-privkeys is specified.
|
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.
|
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.
|
create Makes a new wallet in the file specified by --wallet.
|
||||||
Will complain and require --force if the wallet already exists.
|
Will complain and require --force if the wallet already exists.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user