package org.fisco.bcos.web3j.crypto; import java.math.BigInteger; import java.security.KeyPair; import java.util.Arrays; import java.util.Objects; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.signers.ECDSASigner; import org.bouncycastle.crypto.signers.HMacDSAKCalculator; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.fisco.bcos.web3j.utils.Numeric; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** Elliptic Curve SECP-256k1 generated key pair. */ public class ECKeyPair { private static final Logger logger = LoggerFactory.getLogger(ECKeyPair.class); private final BigInteger privateKey; private final BigInteger publicKey; public ECKeyPair(BigInteger privateKey, BigInteger publicKey) { this.privateKey = privateKey; this.publicKey = publicKey; } public BigInteger getPrivateKey() { return privateKey; } public BigInteger getPublicKey() { return publicKey; } /** * create ECKeyPair from KeyPair * * @param keyPair * @return */ public static ECKeyPair create(KeyPair keyPair) { BCECPrivateKey privateKey = (BCECPrivateKey) keyPair.getPrivate(); BCECPublicKey publicKey = (BCECPublicKey) keyPair.getPublic(); BigInteger privateKeyValue = privateKey.getD(); // Ethereum does not use encoded public keys like bitcoin - see // https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm for details // Additionally, as the first bit is a constant prefix (0x04) we ignore this value byte[] publicKeyBytes = publicKey.getQ().getEncoded(false); BigInteger publicKeyValue = new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 1, publicKeyBytes.length)); ECKeyPair ecKeyPair = new ECKeyPair(privateKeyValue, publicKeyValue); return ecKeyPair; } /** * create ECKeyPair from privateKey * * @param privateKey * @return */ public static ECKeyPair create(BigInteger privateKey) { return new ECKeyPair(privateKey, Sign.publicKeyFromPrivate(privateKey)); } /** * create ECKeyPair from privateKey * * @param privateKey * @return */ public static ECKeyPair create(byte[] privateKey) { return create(Numeric.toBigInt(privateKey)); } /** * Sign a hash with the private key of this key pair. * * @param hash the hash to sign * @return An {@link ECDSASignature} of the hash */ public ECDSASignature sign(byte[] hash) { ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())); ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKey, Sign.CURVE); signer.init(true, privKey); BigInteger[] components = signer.generateSignature(hash); return new ECDSASignature(components[0], components[1]).toCanonicalised(); } /** * Verify a hash with the private key of this key pair. * * @param hash * @param signature * @return */ public boolean verify(byte[] hash, ECDSASignature signature) { ECDSASigner signer = new ECDSASigner(); // not for signing... signer.init( false, new ECPublicKeyParameters( Sign.publicPointFromPrivate(getPrivateKey()), Sign.CURVE)); return signer.verifySignature(hash, signature.r, signature.s); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ECKeyPair ecKeyPair = (ECKeyPair) o; return Objects.equals(privateKey, ecKeyPair.privateKey) && Objects.equals(publicKey, ecKeyPair.publicKey); } @Override public int hashCode() { return Objects.hash(privateKey, publicKey); } }