mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-31 15:22:16 +00:00
Implement BIP38 password encrypted private keys, decryption only. Contains all the test vectors from the spec minus one that is incompatible to Java.
This commit is contained in:
parent
03652298e1
commit
a750a14edd
@ -89,6 +89,9 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
public class ECKey implements EncryptableItem, Serializable {
|
||||
private static final Logger log = LoggerFactory.getLogger(ECKey.class);
|
||||
|
||||
/** The parameters of the secp256k1 curve that Bitcoin uses. */
|
||||
public static final X9ECParameters CURVE_PARAMS = SECNamedCurves.getByName("secp256k1");
|
||||
|
||||
/** The parameters of the secp256k1 curve that Bitcoin uses. */
|
||||
public static final ECDomainParameters CURVE;
|
||||
|
||||
@ -103,9 +106,9 @@ public class ECKey implements EncryptableItem, Serializable {
|
||||
|
||||
static {
|
||||
// All clients must agree on the curve to use by agreement. Bitcoin uses secp256k1.
|
||||
X9ECParameters params = SECNamedCurves.getByName("secp256k1");
|
||||
CURVE = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH());
|
||||
HALF_CURVE_ORDER = params.getN().shiftRight(1);
|
||||
CURVE = new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN(),
|
||||
CURVE_PARAMS.getH());
|
||||
HALF_CURVE_ORDER = CURVE_PARAMS.getN().shiftRight(1);
|
||||
secureRandom = new SecureRandom();
|
||||
}
|
||||
|
||||
@ -178,21 +181,38 @@ public class ECKey implements EncryptableItem, Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ECKey given the private key only. The public key is calculated from it (this is slow). Note that
|
||||
* the resulting public key is compressed.
|
||||
* Creates an ECKey given the private key only. The public key is calculated from it (this is slow). The resulting
|
||||
* public key is compressed.
|
||||
*/
|
||||
public static ECKey fromPrivate(BigInteger privKey) {
|
||||
return new ECKey(privKey, compressPoint(CURVE.getG().multiply(privKey)));
|
||||
return fromPrivate(privKey, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ECKey given the private key only. The public key is calculated from it (this is slow). The resulting
|
||||
* Creates an ECKey given the private key only. The public key is calculated from it (this is slow), either
|
||||
* compressed or not.
|
||||
*/
|
||||
public static ECKey fromPrivate(BigInteger privKey, boolean compressed) {
|
||||
ECPoint point = CURVE.getG().multiply(privKey);
|
||||
return new ECKey(privKey, compressed ? compressPoint(point) : decompressPoint(point));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ECKey given the private key only. The public key is calculated from it (this is slow). The resulting
|
||||
* public key is compressed.
|
||||
*/
|
||||
public static ECKey fromPrivate(byte[] privKeyBytes) {
|
||||
return fromPrivate(new BigInteger(1, privKeyBytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ECKey given the private key only. The public key is calculated from it (this is slow), either
|
||||
* compressed or not.
|
||||
*/
|
||||
public static ECKey fromPrivate(byte[] privKeyBytes, boolean compressed) {
|
||||
return fromPrivate(new BigInteger(1, privKeyBytes), compressed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ECKey that simply trusts the caller to ensure that point is really the result of multiplying the
|
||||
* generator point by the private key. This is used to speed things up when you know you have the right values
|
||||
@ -350,7 +370,7 @@ public class ECKey implements EncryptableItem, Serializable {
|
||||
DERSequenceGenerator seq = new DERSequenceGenerator(baos);
|
||||
seq.addObject(new ASN1Integer(1)); // version
|
||||
seq.addObject(new DEROctetString(privKeyBytes));
|
||||
seq.addObject(new DERTaggedObject(0, SECNamedCurves.getByName("secp256k1").toASN1Primitive()));
|
||||
seq.addObject(new DERTaggedObject(0, CURVE_PARAMS.toASN1Primitive()));
|
||||
seq.addObject(new DERTaggedObject(1, new DERBitString(getPubKey())));
|
||||
seq.close();
|
||||
return baos.toByteArray();
|
||||
|
@ -16,10 +16,14 @@
|
||||
|
||||
package com.google.bitcoin.core;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import org.spongycastle.util.Integers;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* <p>In Bitcoin the following format is often used to represent some type of key:</p>
|
||||
@ -59,10 +63,9 @@ public class VersionedChecksummedBytes implements Serializable {
|
||||
return Base58.encode(addressBytes);
|
||||
}
|
||||
|
||||
// TODO: shouldn't hashCode be also based on the version?
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(bytes);
|
||||
return Objects.hashCode(version, Arrays.hashCode(bytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,7 +73,8 @@ public class VersionedChecksummedBytes implements Serializable {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
VersionedChecksummedBytes other = (VersionedChecksummedBytes) o;
|
||||
return Arrays.equals(bytes, other.bytes);
|
||||
return this.version == other.version
|
||||
&& Arrays.equals(this.bytes, other.bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.bitcoin.crypto;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.Normalizer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import com.google.bitcoin.core.AddressFormatException;
|
||||
import com.google.bitcoin.core.ECKey;
|
||||
import com.google.bitcoin.core.NetworkParameters;
|
||||
import com.google.bitcoin.core.Sha256Hash;
|
||||
import com.google.bitcoin.core.VersionedChecksummedBytes;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.lambdaworks.crypto.SCrypt;
|
||||
|
||||
/**
|
||||
* Implementation of <a href="https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki">BIP 38</a>
|
||||
* passphrase-protected private keys. Currently, only decryption is supported.
|
||||
*/
|
||||
public class BIP38PrivateKey extends VersionedChecksummedBytes {
|
||||
|
||||
public final NetworkParameters params;
|
||||
public final boolean ecMultiply;
|
||||
public final boolean compressed;
|
||||
public final boolean hasLotAndSequence;
|
||||
public final byte[] addressHash;
|
||||
public final byte[] content;
|
||||
|
||||
public static final class BadPassphraseException extends Exception {
|
||||
}
|
||||
|
||||
public BIP38PrivateKey(NetworkParameters params, String encoded) throws AddressFormatException {
|
||||
super(encoded);
|
||||
this.params = params;
|
||||
if (version != 0x01)
|
||||
throw new AddressFormatException("Mismatched version number: " + version);
|
||||
if (bytes.length != 38)
|
||||
throw new AddressFormatException("Wrong number of bytes, excluding version byte: " + bytes.length);
|
||||
hasLotAndSequence = (bytes[1] & 0x04) != 0; // bit 2
|
||||
compressed = (bytes[1] & 0x20) != 0; // bit 5
|
||||
if ((bytes[1] & 0x01) != 0) // bit 0
|
||||
throw new AddressFormatException("Bit 0x40 reserved for future use.");
|
||||
if ((bytes[1] & 0x02) != 0) // bit 1
|
||||
throw new AddressFormatException("Bit 0x80 reserved for future use.");
|
||||
if ((bytes[1] & 0x08) != 0) // bit 3
|
||||
throw new AddressFormatException("Bit 0x08 reserved for future use.");
|
||||
if ((bytes[1] & 0x10) != 0) // bit 4
|
||||
throw new AddressFormatException("Bit 0x10 reserved for future use.");
|
||||
final int byte0 = bytes[0] & 0xff;
|
||||
if (byte0 == 0x42) {
|
||||
// Non-EC-multiplied key
|
||||
if ((bytes[1] & 0xc0) != 0xc0) // bits 6+7
|
||||
throw new AddressFormatException("Bits 0x40 and 0x80 must be set for non-EC-multiplied keys.");
|
||||
ecMultiply = false;
|
||||
if (hasLotAndSequence)
|
||||
throw new AddressFormatException("Non-EC-multiplied keys cannot have lot/sequence.");
|
||||
} else if (byte0 == 0x43) {
|
||||
// EC-multiplied key
|
||||
if ((bytes[1] & 0xc0) != 0x00) // bits 6+7
|
||||
throw new AddressFormatException("Bits 0x40 and 0x80 must be cleared for EC-multiplied keys.");
|
||||
ecMultiply = true;
|
||||
} else {
|
||||
throw new AddressFormatException("Second byte must by 0x42 or 0x43.");
|
||||
}
|
||||
addressHash = Arrays.copyOfRange(bytes, 2, 6);
|
||||
content = Arrays.copyOfRange(bytes, 6, 38);
|
||||
}
|
||||
|
||||
public ECKey decrypt(String passphrase) throws AddressFormatException, BadPassphraseException {
|
||||
String normalizedPassphrase = Normalizer.normalize(passphrase, Normalizer.Form.NFC);
|
||||
ECKey key = ecMultiply ? decryptEC(normalizedPassphrase) : decryptNoEC(normalizedPassphrase);
|
||||
Sha256Hash hash = Sha256Hash.createDouble(key.toAddress(params).toString().getBytes(Charsets.US_ASCII));
|
||||
byte[] actualAddressHash = Arrays.copyOfRange(hash.getBytes(), 0, 4);
|
||||
if (!Arrays.equals(actualAddressHash, addressHash))
|
||||
throw new BadPassphraseException();
|
||||
return key;
|
||||
}
|
||||
|
||||
private ECKey decryptNoEC(String normalizedPassphrase) {
|
||||
try {
|
||||
byte[] derived = SCrypt.scrypt(normalizedPassphrase.getBytes(Charsets.UTF_8), addressHash, 16384, 8, 8, 64);
|
||||
byte[] key = Arrays.copyOfRange(derived, 32, 64);
|
||||
SecretKeySpec keyspec = new SecretKeySpec(key, "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, keyspec);
|
||||
byte[] decrypted = cipher.doFinal(content, 0, 32);
|
||||
for (int i = 0; i < 32; i++)
|
||||
decrypted[i] ^= derived[i];
|
||||
return ECKey.fromPrivate(decrypted, compressed);
|
||||
} catch (GeneralSecurityException x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private ECKey decryptEC(String normalizedPassphrase) {
|
||||
try {
|
||||
byte[] ownerEntropy = Arrays.copyOfRange(content, 0, 8);
|
||||
byte[] ownerSalt = hasLotAndSequence ? Arrays.copyOfRange(ownerEntropy, 0, 4) : ownerEntropy;
|
||||
|
||||
byte[] passFactorBytes = SCrypt.scrypt(normalizedPassphrase.getBytes(Charsets.UTF_8), ownerSalt, 16384, 8, 8, 32);
|
||||
if (hasLotAndSequence) {
|
||||
byte[] hashBytes = Bytes.concat(passFactorBytes, ownerEntropy);
|
||||
checkState(hashBytes.length == 40);
|
||||
passFactorBytes = Sha256Hash.createDouble(hashBytes).getBytes();
|
||||
}
|
||||
BigInteger passFactor = new BigInteger(1, passFactorBytes);
|
||||
ECKey k = ECKey.fromPrivate(passFactor, true);
|
||||
|
||||
byte[] salt = Bytes.concat(addressHash, ownerEntropy);
|
||||
checkState(salt.length == 12);
|
||||
byte[] derived = SCrypt.scrypt(k.getPubKey(), salt, 1024, 1, 1, 64);
|
||||
byte[] aeskey = Arrays.copyOfRange(derived, 32, 64);
|
||||
|
||||
SecretKeySpec keyspec = new SecretKeySpec(aeskey, "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, keyspec);
|
||||
|
||||
byte[] encrypted2 = Arrays.copyOfRange(content, 16, 32);
|
||||
byte[] decrypted2 = cipher.doFinal(encrypted2);
|
||||
checkState(decrypted2.length == 16);
|
||||
for (int i = 0; i < 16; i++)
|
||||
decrypted2[i] ^= derived[i + 16];
|
||||
|
||||
byte[] encrypted1 = Bytes.concat(Arrays.copyOfRange(content, 8, 16), Arrays.copyOfRange(decrypted2, 0, 8));
|
||||
byte[] decrypted1 = cipher.doFinal(encrypted1);
|
||||
checkState(decrypted1.length == 16);
|
||||
for (int i = 0; i < 16; i++)
|
||||
decrypted1[i] ^= derived[i];
|
||||
|
||||
byte[] seed = Bytes.concat(decrypted1, Arrays.copyOfRange(decrypted2, 8, 16));
|
||||
checkState(seed.length == 24);
|
||||
BigInteger seedFactor = new BigInteger(1, Sha256Hash.createDouble(seed).getBytes());
|
||||
checkState(passFactor.signum() >= 0);
|
||||
checkState(seedFactor.signum() >= 0);
|
||||
BigInteger priv = passFactor.multiply(seedFactor).mod(ECKey.CURVE_PARAMS.getN());
|
||||
|
||||
return ECKey.fromPrivate(priv, compressed);
|
||||
} catch (GeneralSecurityException x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
BIP38PrivateKey other = (BIP38PrivateKey) o;
|
||||
|
||||
return super.equals(other)
|
||||
&& Objects.equal(this.params, other.params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(super.hashCode(), params);
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.bitcoin.crypto;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.bitcoin.core.ECKey;
|
||||
import com.google.bitcoin.crypto.BIP38PrivateKey.BadPassphraseException;
|
||||
import com.google.bitcoin.params.MainNetParams;
|
||||
import com.google.bitcoin.params.TestNet3Params;
|
||||
|
||||
public class BIP38PrivateKeyTest {
|
||||
|
||||
private static final MainNetParams MAINNET = MainNetParams.get();
|
||||
private static final TestNet3Params TESTNET = TestNet3Params.get();
|
||||
|
||||
@Test
|
||||
public void bip38testvector_noCompression_noEcMultiply_test1() throws Exception {
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MAINNET,
|
||||
"6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg");
|
||||
ECKey key = encryptedKey.decrypt("TestingOneTwoThree");
|
||||
assertEquals("5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR", key.getPrivateKeyEncoded(MAINNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bip38testvector_noCompression_noEcMultiply_test2() throws Exception {
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MAINNET,
|
||||
"6PRNFFkZc2NZ6dJqFfhRoFNMR9Lnyj7dYGrzdgXXVMXcxoKTePPX1dWByq");
|
||||
ECKey key = encryptedKey.decrypt("Satoshi");
|
||||
assertEquals("5HtasZ6ofTHP6HCwTqTkLDuLQisYPah7aUnSKfC7h4hMUVw2gi5", key.getPrivateKeyEncoded(MAINNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Test disabled because you cannot pass \\u0000 in Strings.")
|
||||
public void bip38testvector_noCompression_noEcMultiply_test3() throws Exception {
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MAINNET,
|
||||
"6PRW5o9FLp4gJDDVqJQKJFTpMvdsSGJxMYHtHaQBF3ooa8mwD69bapcDQn");
|
||||
ECKey key = encryptedKey.decrypt("\u03d2\u0301\u0000\u00010400\u0001f4a9");
|
||||
assertEquals("5Jajm8eQ22H3pGWLEVCXyvND8dQZhiQhoLJNKjYXk9roUFTMSZ4", key.getPrivateKeyEncoded(MAINNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bip38testvector_compression_noEcMultiply_test1() throws Exception {
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(),
|
||||
"6PYNKZ1EAgYgmQfmNVamxyXVWHzK5s6DGhwP4J5o44cvXdoY7sRzhtpUeo");
|
||||
ECKey key = encryptedKey.decrypt("TestingOneTwoThree");
|
||||
assertEquals("L44B5gGEpqEDRS9vVPz7QT35jcBG2r3CZwSwQ4fCewXAhAhqGVpP", key.getPrivateKeyEncoded(MAINNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bip38testvector_compression_noEcMultiply_test2() throws Exception {
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(),
|
||||
"6PYLtMnXvfG3oJde97zRyLYFZCYizPU5T3LwgdYJz1fRhh16bU7u6PPmY7");
|
||||
ECKey key = encryptedKey.decrypt("Satoshi");
|
||||
assertEquals("KwYgW8gcxj1JWJXhPSu4Fqwzfhp5Yfi42mdYmMa4XqK7NJxXUSK7", key.getPrivateKeyEncoded(MAINNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bip38testvector_ecMultiply_noCompression_noLotAndSequence_test1() throws Exception {
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(),
|
||||
"6PfQu77ygVyJLZjfvMLyhLMQbYnu5uguoJJ4kMCLqWwPEdfpwANVS76gTX");
|
||||
ECKey key = encryptedKey.decrypt("TestingOneTwoThree");
|
||||
assertEquals("5K4caxezwjGCGfnoPTZ8tMcJBLB7Jvyjv4xxeacadhq8nLisLR2", key.getPrivateKeyEncoded(MAINNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bip38testvector_ecMultiply_noCompression_noLotAndSequence_test2() throws Exception {
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(),
|
||||
"6PfLGnQs6VZnrNpmVKfjotbnQuaJK4KZoPFrAjx1JMJUa1Ft8gnf5WxfKd");
|
||||
ECKey key = encryptedKey.decrypt("Satoshi");
|
||||
assertEquals("5KJ51SgxWaAYR13zd9ReMhJpwrcX47xTJh2D3fGPG9CM8vkv5sH", key.getPrivateKeyEncoded(MAINNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bip38testvector_ecMultiply_noCompression_lotAndSequence_test1() throws Exception {
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(),
|
||||
"6PgNBNNzDkKdhkT6uJntUXwwzQV8Rr2tZcbkDcuC9DZRsS6AtHts4Ypo1j");
|
||||
ECKey key = encryptedKey.decrypt("MOLON LABE");
|
||||
assertEquals("5JLdxTtcTHcfYcmJsNVy1v2PMDx432JPoYcBTVVRHpPaxUrdtf8", key.getPrivateKeyEncoded(MAINNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bip38testvector_ecMultiply_noCompression_lotAndSequence_test2() throws Exception {
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MainNetParams.get(),
|
||||
"6PgGWtx25kUg8QWvwuJAgorN6k9FbE25rv5dMRwu5SKMnfpfVe5mar2ngH");
|
||||
ECKey key = encryptedKey.decrypt("ΜΟΛΩΝ ΛΑΒΕ");
|
||||
assertEquals("5KMKKuUmAkiNbA3DazMQiLfDq47qs8MAEThm4yL8R2PhV1ov33D", key.getPrivateKeyEncoded(MAINNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bitcoinpaperwallet_testnet() throws Exception {
|
||||
// values taken from bitcoinpaperwallet.com
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(TESTNET,
|
||||
"6PRPhQhmtw6dQu6jD8E1KS4VphwJxBS9Eh9C8FQELcrwN3vPvskv9NKvuL");
|
||||
ECKey key = encryptedKey.decrypt("password");
|
||||
assertEquals("93MLfjbY6ugAsLeQfFY6zodDa8izgm1XAwA9cpMbUTwLkDitopg", key.getPrivateKeyEncoded(TESTNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bitaddress_testnet() throws Exception {
|
||||
// values taken from bitaddress.org
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(TESTNET,
|
||||
"6PfMmVHn153N3x83Yiy4Nf76dHUkXufe2Adr9Fw5bewrunGNeaw2QCpifb");
|
||||
ECKey key = encryptedKey.decrypt("password");
|
||||
assertEquals("91tCpdaGr4Khv7UAuUxa6aMqeN5GcPVJxzLtNsnZHTCndxkRcz2", key.getPrivateKeyEncoded(TESTNET)
|
||||
.toString());
|
||||
}
|
||||
|
||||
@Test(expected = BadPassphraseException.class)
|
||||
public void badPassphrase() throws Exception {
|
||||
BIP38PrivateKey encryptedKey = new BIP38PrivateKey(MAINNET,
|
||||
"6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg");
|
||||
encryptedKey.decrypt("BAD");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJavaSerialization() throws Exception {
|
||||
BIP38PrivateKey key = new BIP38PrivateKey(TESTNET, "6PfMmVHn153N3x83Yiy4Nf76dHUkXufe2Adr9Fw5bewrunGNeaw2QCpifb");
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
new ObjectOutputStream(os).writeObject(key);
|
||||
BIP38PrivateKey keyCopy = (BIP38PrivateKey) new ObjectInputStream(new ByteArrayInputStream(os.toByteArray()))
|
||||
.readObject();
|
||||
assertEquals(key, keyCopy);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user