mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-01-19 05:33:44 +01:00
Implement ASN.1 key export. Patch from Thilo Planz.
Resolves issue 8.
This commit is contained in:
parent
a95949626e
commit
062f87553a
2
AUTHORS
Normal file
2
AUTHORS
Normal file
@ -0,0 +1,2 @@
|
||||
Mike Hearn <hearn@google.com>
|
||||
Thilo Planz <thilo@cpan.org>
|
@ -16,7 +16,20 @@
|
||||
|
||||
package com.google.bitcoin.core;
|
||||
|
||||
import com.google.bitcoin.bouncycastle.asn1.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import com.google.bitcoin.bouncycastle.asn1.ASN1InputStream;
|
||||
import com.google.bitcoin.bouncycastle.asn1.ASN1OutputStream;
|
||||
import com.google.bitcoin.bouncycastle.asn1.DERBitString;
|
||||
import com.google.bitcoin.bouncycastle.asn1.DERInteger;
|
||||
import com.google.bitcoin.bouncycastle.asn1.DEROctetString;
|
||||
import com.google.bitcoin.bouncycastle.asn1.DERSequence;
|
||||
import com.google.bitcoin.bouncycastle.asn1.DERSequenceGenerator;
|
||||
import com.google.bitcoin.bouncycastle.asn1.DERTaggedObject;
|
||||
import com.google.bitcoin.bouncycastle.asn1.sec.SECNamedCurves;
|
||||
import com.google.bitcoin.bouncycastle.asn1.x9.X9ECParameters;
|
||||
import com.google.bitcoin.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||
@ -27,12 +40,6 @@ import com.google.bitcoin.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import com.google.bitcoin.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import com.google.bitcoin.bouncycastle.crypto.signers.ECDSASigner;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Represents an elliptic curve keypair that we own and can use for signing transactions. Currently,
|
||||
* Bouncy Castle is used. In future this may become an interface with multiple implementations using different crypto
|
||||
@ -77,6 +84,34 @@ public class ECKey implements Serializable {
|
||||
return new ECKey(extractPrivateKeyFromASN1(asn1privkey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Output this ECKey as an ASN.1 encoded private key, as understood by OpenSSL or used by the BitCoin reference
|
||||
* implementation in its wallet storage format.
|
||||
*/
|
||||
public byte[] toASN1(){
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(400);
|
||||
ASN1OutputStream encoder = new ASN1OutputStream(baos);
|
||||
|
||||
// ASN1_SEQUENCE(EC_PRIVATEKEY) = {
|
||||
// ASN1_SIMPLE(EC_PRIVATEKEY, version, LONG),
|
||||
// ASN1_SIMPLE(EC_PRIVATEKEY, privateKey, ASN1_OCTET_STRING),
|
||||
// ASN1_EXP_OPT(EC_PRIVATEKEY, parameters, ECPKPARAMETERS, 0),
|
||||
// ASN1_EXP_OPT(EC_PRIVATEKEY, publicKey, ASN1_BIT_STRING, 1)
|
||||
// } ASN1_SEQUENCE_END(EC_PRIVATEKEY)
|
||||
DERSequenceGenerator seq = new DERSequenceGenerator(encoder);
|
||||
seq.addObject(new DERInteger(1)); // version
|
||||
seq.addObject(new DEROctetString(priv.toByteArray()));
|
||||
seq.addObject(new DERTaggedObject(0, SECNamedCurves.getByName("secp256k1").getDERObject()));
|
||||
seq.addObject(new DERTaggedObject(1, new DERBitString(getPubKey())));
|
||||
seq.close();
|
||||
encoder.close();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // Cannot happen, writing to memory stream.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ECKey given only the private key. This works because EC public keys are derivable from their
|
||||
* private keys by doing a multiply with the generator value.
|
||||
|
@ -42,15 +42,24 @@ public class ECKeyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testASNDecode() {
|
||||
byte[] privkeyASN1 = Hex.decode("3082011302010104205c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4aa081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a144034200042af7a2aafe8dafd7dc7f9cfb58ce09bda7dce28653ab229b98d1d3d759660c672dd0db18c8c2d76aa470448e876fc2089ab1354c01a6e72cefc50915f4a963ee");
|
||||
ECKey key = ECKey.fromASN1(privkeyASN1);
|
||||
byte[] message = reverseBytes(Hex.decode("11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a"));
|
||||
public void testASN1Roundtrip() {
|
||||
byte[] privkeyASN1 = Hex.decode(
|
||||
"3082011302010104205c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4aa081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a144034200042af7a2aafe8dafd7dc7f9cfb58ce09bda7dce28653ab229b98d1d3d759660c672dd0db18c8c2d76aa470448e876fc2089ab1354c01a6e72cefc50915f4a963ee");
|
||||
ECKey decodedKey = ECKey.fromASN1(privkeyASN1);
|
||||
|
||||
// Now re-encode and decode the ASN.1 to see if it is equivalent (it does not produce the exact same byte
|
||||
// sequence, some integers are padded now).
|
||||
ECKey roundtripKey = ECKey.fromASN1(decodedKey.toASN1());
|
||||
|
||||
for (ECKey key : new ECKey[] {decodedKey, roundtripKey}) {
|
||||
byte[] message = reverseBytes(Hex.decode(
|
||||
"11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a"));
|
||||
byte[] output = key.sign(message);
|
||||
assertTrue(key.verify(message, output));
|
||||
|
||||
output = Hex.decode
|
||||
("304502206faa2ebc614bf4a0b31f0ce4ed9012eb193302ec2bcaccc7ae8bb40577f47549022100c73a1a1acc209f3f860bf9b9f5e13e9433db6f8b7bd527a088a0e0cd0a4c83e9");
|
||||
output = Hex.decode(
|
||||
"304502206faa2ebc614bf4a0b31f0ce4ed9012eb193302ec2bcaccc7ae8bb40577f47549022100c73a1a1acc209f3f860bf9b9f5e13e9433db6f8b7bd527a088a0e0cd0a4c83e9");
|
||||
assertTrue(key.verify(message, output));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user