3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-12 10:15:52 +00:00

ECKey: preserve compression state when deserializing from ASN.1.

Resolves issue 528.
This commit is contained in:
Mike Hearn 2014-03-17 17:42:56 +01:00
parent f27558bcd2
commit e397928ec3
2 changed files with 20 additions and 7 deletions

View File

@ -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.
} }

View File

@ -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
@ -367,7 +371,7 @@ public class ECKeyTest {
public void keyRecoveryWithEncryptedKey() throws Exception { public void keyRecoveryWithEncryptedKey() throws Exception {
ECKey unencryptedKey = new ECKey(); ECKey unencryptedKey = new ECKey();
KeyParameter aesKey = keyCrypter.deriveKey(PASSWORD1); KeyParameter aesKey = keyCrypter.deriveKey(PASSWORD1);
ECKey encryptedKey = unencryptedKey.encrypt(keyCrypter,aesKey); ECKey encryptedKey = unencryptedKey.encrypt(keyCrypter, aesKey);
String message = "Goodbye Jupiter!"; String message = "Goodbye Jupiter!";
Sha256Hash hash = Sha256Hash.create(message.getBytes()); Sha256Hash hash = Sha256Hash.create(message.getBytes());