/**
* Copyright 2011 Google Inc.
*
* 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.core;
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;
import com.google.bitcoin.bouncycastle.crypto.generators.ECKeyPairGenerator;
import com.google.bitcoin.bouncycastle.crypto.params.ECDomainParameters;
import com.google.bitcoin.bouncycastle.crypto.params.ECKeyGenerationParameters;
import com.google.bitcoin.bouncycastle.crypto.params.ECPrivateKeyParameters;
import com.google.bitcoin.bouncycastle.crypto.params.ECPublicKeyParameters;
import com.google.bitcoin.bouncycastle.crypto.signers.ECDSASigner;
/**
* 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
* libraries. The class also provides a static method that can verify a signature with just the public key.<p>
*/
public class ECKey implements Serializable {
private static final ECDomainParameters ecParams;
private static final SecureRandom secureRandom;
private static final long serialVersionUID = -728224901792295832L;
static {
// All clients must agree on the curve to use by agreement. BitCoin uses secp256k1.
X9ECParameters params = SECNamedCurves.getByName("secp256k1");
ecParams = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH());
secureRandom = new SecureRandom();
}
private final BigInteger priv;
private final byte[] pub;
transient private byte[] pubKeyHash;
/** Generates an entirely new keypair. */
public ECKey() {
ECKeyPairGenerator generator = new ECKeyPairGenerator();
ECKeyGenerationParameters keygenParams = new ECKeyGenerationParameters(ecParams, secureRandom);
generator.init(keygenParams);
AsymmetricCipherKeyPair keypair = generator.generateKeyPair();
ECPrivateKeyParameters privParams = (ECPrivateKeyParameters) keypair.getPrivate();
ECPublicKeyParameters pubParams = (ECPublicKeyParameters) keypair.getPublic();
priv = privParams.getD();
// The public key is an encoded point on the elliptic curve. It has no meaning independent of the curve.
pub = pubParams.getQ().getEncoded();
}
/**
* Construct an ECKey from an ASN.1 encoded private key. These are produced by OpenSSL and stored by the BitCoin
* reference implementation in its wallet.
*/
public static ECKey fromASN1(byte[] asn1privkey) {
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.
*/
public ECKey(BigInteger privKey) {
this.priv = privKey;
this.pub = publicKeyFromPrivate(privKey);
}
/** Derive the public key by doing a point multiply of G * priv. */
private static byte[] publicKeyFromPrivate(BigInteger privKey) {
return ecParams.getG().multiply(privKey).getEncoded();
}
/** Gets the hash160 form of the public key (as seen in addresses). */
public byte[] getPubKeyHash() {
if (pubKeyHash == null)
pubKeyHash = Utils.sha256hash160(this.pub);
return pubKeyHash;
}
/**
* Gets the raw public key value. This appears in transaction scriptSigs. Note that this is <b>not</b> the same
* as the pubKeyHash/address.
*/
public byte[] getPubKey() {
return pub;
}
public String toString() {
StringBuffer b = new StringBuffer();
b.append("pub:").append(Utils.bytesToHexString(pub));
b.append(" priv:").append(Utils.bytesToHexString(priv.toByteArray()));
return b.toString();
}
/**
* Returns the address that corresponds to the public part of this ECKey. Note that an address is derived from
* the RIPEMD-160 hash of the public key and is not the public key itself (which is too large to be convenient).
* @throws AddressFormatException
*/
public Address toAddress(NetworkParameters params) throws AddressFormatException {
byte[] hash160 = Utils.sha256hash160(pub);
return new Address(params, hash160);
}
/**
* Calcuates an ECDSA signature in DER format for the given input hash. Note that the input is expected to be
* 32 bytes long.
*/
public byte[] sign(byte[] input) {
ECDSASigner signer = new ECDSASigner();
ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(priv, ecParams);
signer.init(true, privKey);
BigInteger[] sigs = signer.generateSignature(input);
// What we get back from the signer are the two components of a signature, r and s. To get a flat byte stream
// of the type used by BitCoin we have to encode them using DER encoding, which is just a way to pack the two
// components into a structure.
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DERSequenceGenerator seq = new DERSequenceGenerator(bos);
seq.addObject(new DERInteger(sigs[0]));
seq.addObject(new DERInteger(sigs[1]));
seq.close();
return bos.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e); // Cannot happen.
}
}
/**
* Verifies the given ASN.1 encoded ECDSA signature against a hash using the public key.
* @param data Hash of the data to verify.
* @param signature ASN.1 encoded signature.
* @param pub The public key bytes to use.
*/
public static boolean verify(byte[] data, byte[] signature, byte[] pub) {
ECDSASigner signer = new ECDSASigner();
ECPublicKeyParameters params = new ECPublicKeyParameters(ecParams.getCurve().decodePoint(pub), ecParams);
signer.init(false, params);
try {
ASN1InputStream decoder = new ASN1InputStream(signature);
DERSequence seq = (DERSequence) decoder.readObject();
DERInteger r = (DERInteger) seq.getObjectAt(0);
DERInteger s = (DERInteger) seq.getObjectAt(1);
decoder.close();
return signer.verifySignature(data, r.getValue(), s.getValue());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Verifies the given ASN.1 encoded ECDSA signature against a hash using the public key.
* @param data Hash of the data to verify.
* @param signature ASN.1 encoded signature.
*/
public boolean verify(byte[] data, byte[] signature) {
return ECKey.verify(data, signature, pub);
}
private static BigInteger extractPrivateKeyFromASN1(byte[] asn1privkey) {
// 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:
//
// 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)
//
try {
ASN1InputStream decoder = new ASN1InputStream(asn1privkey);
DERSequence seq = (DERSequence) decoder.readObject();
assert seq.size() == 4 : "Input does not appear to be an ASN.1 OpenSSL EC private key";
assert ((DERInteger) seq.getObjectAt(0)).getValue().equals(BigInteger.ONE) : "Input is of wrong version";
DEROctetString key = (DEROctetString) seq.getObjectAt(1);
decoder.close();
return new BigInteger(key.getOctets());
} catch (IOException e) {
throw new RuntimeException(e); // Cannot happen, reading from memory stream.
}
}
}