mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-02-25 15:10:18 +01:00
ECKey: preserve compression state when deserializing from ASN.1.
Resolves issue 528.
This commit is contained in:
parent
f27558bcd2
commit
e397928ec3
2 changed files with 20 additions and 7 deletions
|
@ -143,7 +143,7 @@ public class ECKey implements Serializable {
|
||||||
* reference implementation in its wallet. Note that this is slow because it requires an EC point multiply.
|
* reference implementation in its wallet. Note that this is slow because it requires an EC point multiply.
|
||||||
*/
|
*/
|
||||||
public static ECKey fromASN1(byte[] asn1privkey) {
|
public static ECKey fromASN1(byte[] asn1privkey) {
|
||||||
return new ECKey(extractPrivateKeyFromASN1(asn1privkey));
|
return extractKeyFromASN1(asn1privkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates an ECKey given the private key only. The public key is calculated from it (this is slow) */
|
/** Creates an ECKey given the private key only. The public key is calculated from it (this is slow) */
|
||||||
|
@ -559,7 +559,7 @@ public class ECKey implements Serializable {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BigInteger extractPrivateKeyFromASN1(byte[] asn1privkey) {
|
private static ECKey extractKeyFromASN1(byte[] asn1privkey) {
|
||||||
// To understand this code, see the definition of the ASN.1 format for EC private keys in the OpenSSL source
|
// To understand this code, see the definition of the ASN.1 format for EC private keys in the OpenSSL source
|
||||||
// code in ec_asn1.c:
|
// code in ec_asn1.c:
|
||||||
//
|
//
|
||||||
|
@ -577,9 +577,18 @@ public class ECKey implements Serializable {
|
||||||
checkArgument(((DERInteger) seq.getObjectAt(0)).getValue().equals(BigInteger.ONE),
|
checkArgument(((DERInteger) seq.getObjectAt(0)).getValue().equals(BigInteger.ONE),
|
||||||
"Input is of wrong version");
|
"Input is of wrong version");
|
||||||
Object obj = seq.getObjectAt(1);
|
Object obj = seq.getObjectAt(1);
|
||||||
byte[] bits = ((ASN1OctetString) obj).getOctets();
|
byte[] privbits = ((ASN1OctetString) obj).getOctets();
|
||||||
decoder.close();
|
decoder.close();
|
||||||
return new BigInteger(1, bits);
|
BigInteger privkey = new BigInteger(1, privbits);
|
||||||
|
byte[] pubbits = ((DERBitString)((ASN1TaggedObject)seq.getObjectAt(3)).getObject()).getBytes();
|
||||||
|
// Now sanity check to ensure the pubkey bytes match the privkey.
|
||||||
|
byte[] compressed = publicKeyFromPrivate(privkey, true);
|
||||||
|
if (Arrays.equals(pubbits, compressed))
|
||||||
|
return new ECKey(privkey, compressed);
|
||||||
|
byte[] uncompressed = publicKeyFromPrivate(privkey, false);
|
||||||
|
if (Arrays.equals(pubbits, uncompressed))
|
||||||
|
return new ECKey(privkey, uncompressed);
|
||||||
|
throw new IllegalArgumentException("Public key in ASN.1 structure does not match private key.");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e); // Cannot happen, reading from memory stream.
|
throw new RuntimeException(e); // Cannot happen, reading from memory stream.
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,15 +39,14 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import static com.google.bitcoin.core.Utils.reverseBytes;
|
import static com.google.bitcoin.core.Utils.reverseBytes;
|
||||||
|
@ -171,6 +170,11 @@ public class ECKeyTest {
|
||||||
"11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a"));
|
"11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a"));
|
||||||
assertTrue(roundtripKey.verify(message, decodedKey.sign(new Sha256Hash(message)).encodeToDER()));
|
assertTrue(roundtripKey.verify(message, decodedKey.sign(new Sha256Hash(message)).encodeToDER()));
|
||||||
assertTrue(decodedKey.verify(message, roundtripKey.sign(new Sha256Hash(message)).encodeToDER()));
|
assertTrue(decodedKey.verify(message, roundtripKey.sign(new Sha256Hash(message)).encodeToDER()));
|
||||||
|
|
||||||
|
// Verify bytewise equivalence of public keys (i.e. compression state is preserved)
|
||||||
|
ECKey key = new ECKey();
|
||||||
|
ECKey key2 = ECKey.fromASN1(key.toASN1());
|
||||||
|
assertArrayEquals(key.getPubKey(), key2.getPubKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Add table
Reference in a new issue