/* * Copyright (c) [2016] [ <ether.camp> ] * This file is part of the ethereumJ library. * * The ethereumJ library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ethereumJ library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>. */ package org.gsc.crypto; import static org.gsc.utils.ByteUtil.bigIntegerToBytes; import java.io.IOException; import java.io.Serializable; import java.math.BigInteger; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Signature; import java.security.SignatureException; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.InvalidKeySpecException; import java.util.Arrays; import javax.annotation.Nullable; import javax.crypto.KeyAgreement; import lombok.extern.slf4j.Slf4j; import org.spongycastle.asn1.ASN1InputStream; import org.spongycastle.asn1.ASN1Integer; import org.spongycastle.asn1.DLSequence; import org.spongycastle.asn1.sec.SECNamedCurves; import org.spongycastle.asn1.x9.X9ECParameters; import org.spongycastle.asn1.x9.X9IntegerConverter; import org.spongycastle.crypto.agreement.ECDHBasicAgreement; import org.spongycastle.crypto.digests.SHA256Digest; import org.spongycastle.crypto.engines.AESEngine; import org.spongycastle.crypto.modes.SICBlockCipher; import org.spongycastle.crypto.params.ECDomainParameters; import org.spongycastle.crypto.params.ECPrivateKeyParameters; import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.ParametersWithIV; import org.spongycastle.crypto.signers.ECDSASigner; import org.spongycastle.crypto.signers.HMacDSAKCalculator; import org.spongycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.spongycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.spongycastle.jce.spec.ECParameterSpec; import org.spongycastle.jce.spec.ECPrivateKeySpec; import org.spongycastle.jce.spec.ECPublicKeySpec; import org.spongycastle.math.ec.ECAlgorithms; import org.spongycastle.math.ec.ECCurve; import org.spongycastle.math.ec.ECPoint; import org.spongycastle.util.BigIntegers; import org.spongycastle.util.encoders.Base64; import org.spongycastle.util.encoders.Hex; import org.gsc.crypto.jce.ECKeyAgreement; import org.gsc.crypto.jce.ECKeyFactory; import org.gsc.crypto.jce.ECKeyPairGenerator; import org.gsc.crypto.jce.ECSignatureFactory; import org.gsc.crypto.jce.GSCCastleProvider; import org.gsc.utils.ByteUtil; import org.gsc.utils.BIUtil; @Slf4j(topic = "crypto") public class ECKey implements Serializable { /** * The parameters of the secp256k1 curve. */ public static final ECDomainParameters CURVE; public static final ECParameterSpec CURVE_SPEC; /** * Equal to CURVE.getN().shiftRight(1), used for canonicalising the S value of a signature. ECDSA * signatures are mutable in the sense that for a given (R, S) pair, then both (R, S) and (R, N - * S mod N) are valid signatures. Canonical signatures are those where 1 <= S <= N/2 * * <p>See https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki * #Low_S_values_in_signatures */ public static final BigInteger HALF_CURVE_ORDER; private static final BigInteger SECP256K1N = new BigInteger("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16); private static final SecureRandom secureRandom; private static final long serialVersionUID = -728224901792295832L; static { // All clients must agree on the curve to use by agreement. X9ECParameters params = SECNamedCurves.getByName("secp256k1"); CURVE = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); CURVE_SPEC = new ECParameterSpec(params.getCurve(), params.getG(), params.getN(), params.getH()); HALF_CURVE_ORDER = params.getN().shiftRight(1); secureRandom = new SecureRandom(); } protected final ECPoint pub; // The two parts of the key. If "priv" is set, "pub" can always be // calculated. If "pub" is set but not "priv", we // can only verify signatures not make them. // TODO: Redesign this class to use consistent internals and more // efficient serialization. private final PrivateKey privKey; // the Java Cryptographic Architecture provider to use for Signature // this is set along with the PrivateKey privKey and must be compatible // this provider will be used when selecting a Signature instance // https://docs.oracle.com/javase/8/docs/technotes/guides/security // /SunProviders.html private final Provider provider; // Transient because it's calculated on demand. private transient byte[] pubKeyHash; private transient byte[] nodeId; /** * Generates an entirely new keypair. * * <p>BouncyCastle will be used as the Java Security Provider */ public ECKey() { this(secureRandom); } /** * Generate a new keypair using the given Java Security Provider. * * <p>All private key operations will use the provider. */ public ECKey(Provider provider, SecureRandom secureRandom) { this.provider = provider; final KeyPairGenerator keyPairGen = ECKeyPairGenerator.getInstance(provider, secureRandom); final KeyPair keyPair = keyPairGen.generateKeyPair(); this.privKey = keyPair.getPrivate(); final PublicKey pubKey = keyPair.getPublic(); if (pubKey instanceof BCECPublicKey) { pub = ((BCECPublicKey) pubKey).getQ(); } else if (pubKey instanceof ECPublicKey) { pub = extractPublicKey((ECPublicKey) pubKey); } else { throw new AssertionError( "Expected Provider " + provider.getName() + " to produce a subtype of ECPublicKey, found " + pubKey.getClass()); } } /** * Generates an entirely new keypair with the given {@link SecureRandom} object. <p> BouncyCastle * will be used as the Java Security Provider * * @param secureRandom - */ public ECKey(SecureRandom secureRandom) { this(GSCCastleProvider.getInstance(), secureRandom); } /** * Pair a private key with a public EC point. * * <p>All private key operations will use the provider. */ public ECKey(Provider provider, @Nullable PrivateKey privKey, ECPoint pub) { this.provider = provider; if (privKey == null || isECPrivateKey(privKey)) { this.privKey = privKey; } else { throw new IllegalArgumentException( "Expected EC private key, given a private key object with" + " class " + privKey.getClass().toString() + " and algorithm " + privKey.getAlgorithm()); } if (pub == null) { throw new IllegalArgumentException("Public key may not be null"); } else { this.pub = pub; } } /** * Pair a private key integer with a public EC point <p> BouncyCastle will be used as the Java * Security Provider */ public ECKey(@Nullable BigInteger priv, ECPoint pub) { this( GSCCastleProvider.getInstance(), privateKeyFromBigInteger(priv), pub ); } /* Convert a Java JCE ECPublicKey into a BouncyCastle ECPoint */ private static ECPoint extractPublicKey(final ECPublicKey ecPublicKey) { final java.security.spec.ECPoint publicPointW = ecPublicKey.getW(); final BigInteger xCoord = publicPointW.getAffineX(); final BigInteger yCoord = publicPointW.getAffineY(); return CURVE.getCurve().createPoint(xCoord, yCoord); } /* Test if a generic private key is an EC private key * * it is not sufficient to check that privKey is a subtype of ECPrivateKey * as the SunPKCS11 Provider will return a generic PrivateKey instance * a fallback that covers this case is to check the key algorithm */ private static boolean isECPrivateKey(PrivateKey privKey) { return privKey instanceof ECPrivateKey || privKey.getAlgorithm() .equals("EC"); } /* Convert a BigInteger into a PrivateKey object */ private static PrivateKey privateKeyFromBigInteger(BigInteger priv) { if (priv == null) { return null; } else { try { return ECKeyFactory .getInstance(GSCCastleProvider.getInstance()) .generatePrivate(new ECPrivateKeySpec(priv, CURVE_SPEC)); } catch (InvalidKeySpecException ex) { throw new AssertionError("Assumed correct key spec statically"); } } } /** * Utility for compressing an elliptic curve point. Returns the same point if it's already * compressed. See the ECKey class docs for a discussion of point compression. * * @param uncompressed - * @return - * @deprecated per-point compression property will be removed in Bouncy Castle */ public static ECPoint compressPoint(ECPoint uncompressed) { return CURVE.getCurve().decodePoint(uncompressed.getEncoded(true)); } /** * Utility for decompressing an elliptic curve point. Returns the same point if it's already * compressed. See the ECKey class docs for a discussion of point compression. * * @param compressed - * @return - * @deprecated per-point compression property will be removed in Bouncy Castle */ public static ECPoint decompressPoint(ECPoint compressed) { return CURVE.getCurve().decodePoint(compressed.getEncoded(false)); } /** * Creates an ECKey given the private key only. * * @param privKey - * @return - */ public static ECKey fromPrivate(BigInteger privKey) { return new ECKey(privKey, CURVE.getG().multiply(privKey)); } /** * Creates an ECKey given the private key only. * * @param privKeyBytes - * @return - */ public static ECKey fromPrivate(byte[] privKeyBytes) { return fromPrivate(new BigInteger(1, privKeyBytes)); } /** * Creates an ECKey that simply trusts the caller to ensure that point is really the result of * multiplying the generator point by the private key. This is used to speed things up when you * know you have the right values already. The compression state of pub will be preserved. * * @param priv - * @param pub - * @return - */ public static ECKey fromPrivateAndPrecalculatedPublic(BigInteger priv, ECPoint pub) { return new ECKey(priv, pub); } /** * Creates an ECKey that simply trusts the caller to ensure that point is really the result of * multiplying the generator point by the private key. This is used to speed things up when you * know you have the right values already. The compression state of the point will be preserved. * * @param priv - * @param pub - * @return - */ public static ECKey fromPrivateAndPrecalculatedPublic(byte[] priv, byte[] pub) { check(priv != null, "Private key must not be null"); check(pub != null, "Public key must not be null"); return new ECKey(new BigInteger(1, priv), CURVE.getCurve() .decodePoint(pub)); } /** * Creates an ECKey that cannot be used for signing, only verifying signatures, from the given * point. The compression state of pub will be preserved. * * @param pub - * @return - */ public static ECKey fromPublicOnly(ECPoint pub) { return new ECKey(null, pub); } /** * Creates an ECKey that cannot be used for signing, only verifying signatures, from the given * encoded point. The compression state of pub will be preserved. * * @param pub - * @return - */ public static ECKey fromPublicOnly(byte[] pub) { return new ECKey(null, CURVE.getCurve().decodePoint(pub)); } /** * Returns public key bytes from the given private key. To convert a byte array into a BigInteger, * use <tt> new BigInteger(1, bytes);</tt> * * @param privKey - * @param compressed - * @return - */ public static byte[] publicKeyFromPrivate(BigInteger privKey, boolean compressed) { ECPoint point = CURVE.getG().multiply(privKey); return point.getEncoded(compressed); } /** * Compute an address from an encoded public key. * * @param pubBytes an encoded (uncompressed) public key * @return 23-byte address */ public static byte[] computeAddress(byte[] pubBytes) { return Hash.sha3omit12( Arrays.copyOfRange(pubBytes, 1, pubBytes.length)); } /** * Compute an address from a public point. * * @param pubPoint a public point * @return 23-byte address */ public static byte[] computeAddress(ECPoint pubPoint) { return computeAddress(pubPoint.getEncoded(/* uncompressed */ false)); } /** * Compute the encoded X, Y coordinates of a public point. <p> This is the encoded public key * without the leading byte. * * @param pubPoint a public point * @return 64-byte X,Y point pair */ public static byte[] pubBytesWithoutFormat(ECPoint pubPoint) { final byte[] pubBytes = pubPoint.getEncoded(/* uncompressed */ false); return Arrays.copyOfRange(pubBytes, 1, pubBytes.length); } /** * Recover the public key from an encoded node id. * * @param nodeId a 64-byte X,Y point pair */ public static ECKey fromNodeId(byte[] nodeId) { check(nodeId.length == 64, "Expected a 64 byte node id"); byte[] pubBytes = new byte[65]; System.arraycopy(nodeId, 0, pubBytes, 1, nodeId.length); pubBytes[0] = 0x04; // uncompressed return ECKey.fromPublicOnly(pubBytes); } public static byte[] signatureToKeyBytes(byte[] messageHash, String signatureBase64) throws SignatureException { byte[] signatureEncoded; try { signatureEncoded = Base64.decode(signatureBase64); } catch (RuntimeException e) { // This is what you getData back from Bouncy Castle if base64 doesn't // decode :( throw new SignatureException("Could not decode base64", e); } // Parse the signature bytes into r/s and the selector value. if (signatureEncoded.length < 65) { throw new SignatureException("Signature truncated, expected 65 " + "bytes and got " + signatureEncoded.length); } return signatureToKeyBytes( messageHash, ECDSASignature.fromComponents( Arrays.copyOfRange(signatureEncoded, 1, 33), Arrays.copyOfRange(signatureEncoded, 33, 65), (byte) (signatureEncoded[0] & 0xFF))); } public static byte[] signatureToKeyBytes(byte[] messageHash, ECDSASignature sig) throws SignatureException { check(messageHash.length == 32, "messageHash argument has length " + messageHash.length); int header = sig.v; // The header byte: 0x1B = first key with even y, 0x1C = first key // with odd y, // 0x1D = second key with even y, 0x1E = second key // with odd y if (header < 27 || header > 34) { throw new SignatureException("Header byte out of range: " + header); } if (header >= 31) { header -= 4; } int recId = header - 27; byte[] key = ECKey.recoverPubBytesFromSignature(recId, sig, messageHash); if (key == null) { throw new SignatureException("Could not recover public key from " + "signature"); } return key; } /** * Compute the address of the key that signed the given signature. * * @param messageHash 32-byte hash of message * @param signatureBase64 Base-64 encoded signature * @return 20-byte address */ public static byte[] signatureToAddress(byte[] messageHash, String signatureBase64) throws SignatureException { return computeAddress(signatureToKeyBytes(messageHash, signatureBase64)); } /** * Compute the address of the key that signed the given signature. * * @param messageHash 32-byte hash of message * @param sig - * @return 20-byte address */ public static byte[] signatureToAddress(byte[] messageHash, ECDSASignature sig) throws SignatureException { return computeAddress(signatureToKeyBytes(messageHash, sig)); } /** * Compute the key that signed the given signature. * * @param messageHash 32-byte hash of message * @param signatureBase64 Base-64 encoded signature * @return ECKey */ public static ECKey signatureToKey(byte[] messageHash, String signatureBase64) throws SignatureException { final byte[] keyBytes = signatureToKeyBytes(messageHash, signatureBase64); return ECKey.fromPublicOnly(keyBytes); } /** * Compute the key that signed the given signature. * * @param messageHash 32-byte hash of message * @param sig - * @return ECKey */ public static ECKey signatureToKey(byte[] messageHash, ECDSASignature sig) throws SignatureException { final byte[] keyBytes = signatureToKeyBytes(messageHash, sig); return ECKey.fromPublicOnly(keyBytes); } /** * <p>Verifies the given ECDSA signature against the message bytes using the public key bytes.</p> * <p> <p>When using native ECDSA verification, data must be 32 bytes, and no element may be * larger than 520 bytes.</p> * * @param data Hash of the data to verify. * @param signature signature. * @param pub The public key bytes to use. * @return - */ public static boolean verify(byte[] data, ECDSASignature signature, byte[] pub) { ECDSASigner signer = new ECDSASigner(); ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE .getCurve().decodePoint(pub), CURVE); signer.init(false, params); try { return signer.verifySignature(data, signature.r, signature.s); } catch (NullPointerException npe) { // Bouncy Castle contains a bug that can cause NPEs given // specially crafted signatures. // Those signatures are inherently invalid/attack sigs so we just // fail them here rather than crash the thread. logger.error("Caught NPE inside bouncy castle", npe); return false; } } /** * 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 signature. * @param pub The public key bytes to use. * @return - */ public static boolean verify(byte[] data, byte[] signature, byte[] pub) { return verify(data, ECDSASignature.decodeFromDER(signature), pub); } /** * Returns true if the given pubkey is canonical, i.e. the correct length taking into account * compression. * * @param pubkey - * @return - */ public static boolean isPubKeyCanonical(byte[] pubkey) { if (pubkey[0] == 0x04) { // Uncompressed pubkey return pubkey.length == 65; } else if (pubkey[0] == 0x02 || pubkey[0] == 0x03) { // Compressed pubkey return pubkey.length == 33; } else { return false; } } /** * <p>Given the components of a signature and a selector value, recover and return the public key * that generated the signature according to the algorithm in SEC1v2 section 4.1.6.</p> * * <p> <p>The recId is an index from 0 to 3 which indicates which of the 4 possible allKeys is the * correct one. Because the key recovery operation yields multiple potential allKeys, the correct * key must either be stored alongside the signature, or you must be willing to try each recId in * turn until you find one that outputs the key you are expecting.</p> * * <p> <p>If this method returns null it means recovery was not possible and recId should be * iterated.</p> * * <p> <p>Given the above two points, a correct usage of this method is inside a for loop from 0 * to 3, and if the output is null OR a key that is not the one you expect, you try again with the * next recId.</p> * * @param recId Which possible key to recover. * @param sig the R and S components of the signature, wrapped. * @param messageHash Hash of the data that was signed. * @return 65-byte encoded public key */ @Nullable public static byte[] recoverPubBytesFromSignature(int recId, ECDSASignature sig, byte[] messageHash) { check(recId >= 0, "recId must be positive"); check(sig.r.signum() >= 0, "r must be positive"); check(sig.s.signum() >= 0, "s must be positive"); check(messageHash != null, "messageHash must not be null"); // 1.0 For j from 0 to h (h == recId here and the loop is outside // this function) // 1.1 Let x = r + jn BigInteger n = CURVE.getN(); // Curve order. BigInteger i = BigInteger.valueOf((long) recId / 2); BigInteger x = sig.r.add(i.multiply(n)); // 1.2. Convert the integer x to an octet string X of length mlen // using the conversion routine // specified in Section 2.3.7, where mlen = ⌈(log2 p)/8⌉ or // mlen = ⌈m/8⌉. // 1.3. Convert the octet string (16 set binary digits)||X to an // elliptic curve point R using the // conversion routine specified in Section 2.3.4. If this // conversion routine outputs “invalid”, then // do another iteration of Step 1. // // More concisely, what these points mean is to use X as a compressed // public key. ECCurve.Fp curve = (ECCurve.Fp) CURVE.getCurve(); BigInteger prime = curve.getQ(); // Bouncy Castle is not consistent // about the letter it uses for the prime. if (x.compareTo(prime) >= 0) { // Cannot have point co-ordinates larger than this as everything // takes place modulo Q. return null; } // Compressed allKeys require you to know an extra bit of data about the // y-coord as there are two possibilities. // So it's encoded in the recId. ECPoint R = decompressKey(x, (recId & 1) == 1); // 1.4. If nR != point at infinity, then do another iteration of // Step 1 (callers responsibility). if (!R.multiply(n).isInfinity()) { return null; } // 1.5. Compute e from M using Steps 2 and 3 of ECDSA signature // verification. BigInteger e = new BigInteger(1, messageHash); // 1.6. For k from 1 to 2 do the following. (loop is outside this // function via iterating recId) // 1.6.1. Compute a candidate public key as: // Q = mi(r) * (sR - eG) // // Where mi(x) is the modular multiplicative inverse. We transform // this into the following: // Q = (mi(r) * s ** R) + (mi(r) * -e ** G) // Where -e is the modular additive inverse of e, that is z such that // z + e = 0 (mod n). In the above equation // ** is point multiplication and + is point addition (the EC group // operator). // // We can find the additive inverse by subtracting e from zero then // taking the mod. For example the additive // inverse of 3 modulo 11 is 8 because 3 + 8 mod 11 = 0, and -3 mod // 11 = 8. BigInteger eInv = BigInteger.ZERO.subtract(e).mod(n); BigInteger rInv = sig.r.modInverse(n); BigInteger srInv = rInv.multiply(sig.s).mod(n); BigInteger eInvrInv = rInv.multiply(eInv).mod(n); ECPoint.Fp q = (ECPoint.Fp) ECAlgorithms.sumOfTwoMultiplies(CURVE .getG(), eInvrInv, R, srInv); return q.getEncoded(/* compressed */ false); } /** * @param recId Which possible key to recover. * @param sig the R and S components of the signature, wrapped. * @param messageHash Hash of the data that was signed. * @return 20-byte address */ @Nullable public static byte[] recoverAddressFromSignature(int recId, ECDSASignature sig, byte[] messageHash) { final byte[] pubBytes = recoverPubBytesFromSignature(recId, sig, messageHash); if (pubBytes == null) { return null; } else { return computeAddress(pubBytes); } } /** * @param recId Which possible key to recover. * @param sig the R and S components of the signature, wrapped. * @param messageHash Hash of the data that was signed. * @return ECKey */ @Nullable public static ECKey recoverFromSignature(int recId, ECDSASignature sig, byte[] messageHash) { final byte[] pubBytes = recoverPubBytesFromSignature(recId, sig, messageHash); if (pubBytes == null) { return null; } else { return ECKey.fromPublicOnly(pubBytes); } } /** * Decompress a compressed public key (x co-ord and low-bit of y-coord). * * @param xBN - * @param yBit - * @return - */ private static ECPoint decompressKey(BigInteger xBN, boolean yBit) { X9IntegerConverter x9 = new X9IntegerConverter(); byte[] compEnc = x9.integerToBytes(xBN, 1 + x9.getByteLength(CURVE .getCurve())); compEnc[0] = (byte) (yBit ? 0x03 : 0x02); return CURVE.getCurve().decodePoint(compEnc); } private static void check(boolean test, String message) { if (!test) { throw new IllegalArgumentException(message); } } /** * Returns a copy of this key, but with the public point represented in uncompressed form. * Normally you would never need this: it's for specialised scenarios or when backwards * compatibility in encoded form is necessary. * * @return - * @deprecated per-point compression property will be removed in Bouncy Castle */ public ECKey decompress() { if (!pub.isCompressed()) { return this; } else { return new ECKey(this.provider, this.privKey, decompressPoint(pub)); } } /** * @deprecated per-point compression property will be removed in Bouncy Castle */ public ECKey compress() { if (pub.isCompressed()) { return this; } else { return new ECKey(this.provider, this.privKey, compressPoint(pub)); } } /** * Returns true if this key doesn't have access to private key bytes. This may be because it was * never given any private key bytes to begin with (a watching key). * * @return - */ public boolean isPubKeyOnly() { return privKey == null; } /** * Returns true if this key has access to private key bytes. Does the opposite of {@link * #isPubKeyOnly()}. * * @return - */ public boolean hasPrivKey() { return privKey != null; } /** * Gets the address form of the public key. * * @return 23-byte address */ public byte[] getAddress() { if (pubKeyHash == null) { pubKeyHash = computeAddress(this.pub); } return pubKeyHash; } /** * Generates the NodeID based on this key, that is the public key without first format byte */ public byte[] getNodeId() { if (nodeId == null) { nodeId = pubBytesWithoutFormat(this.pub); } return nodeId; } /** * Gets the encoded public key value. * * @return 65-byte encoded public key */ public byte[] getPubKey() { return pub.getEncoded(/* compressed */ false); } /** * Gets the public key in the form of an elliptic curve point object from Bouncy Castle. * * @return - */ public ECPoint getPubKeyPoint() { return pub; } /** * Gets the private key in the form of an integer field element. The public key is derived by * performing EC point addition this number of times (i.e. point multiplying). * * @return - * @throws IllegalStateException if the private key bytes are not available. */ public BigInteger getPrivKey() { if (privKey == null) { throw new MissingPrivateKeyException(); } else if (privKey instanceof BCECPrivateKey) { return ((BCECPrivateKey) privKey).getD(); } else { throw new MissingPrivateKeyException(); } } /** * Returns whether this key is using the compressed form or not. Compressed pubkeys are only 33 * bytes, not 64. * * @return - */ public boolean isCompressed() { return pub.isCompressed(); } public String toString() { StringBuilder b = new StringBuilder(); b.append("pub:").append(Hex.toHexString(pub.getEncoded(false))); return b.toString(); } /** * Produce a string rendering of the ECKey INCLUDING the private key. Unless you absolutely need * the private key it is better for security reasons to just use toString(). * * @return - */ public String toStringWithPrivate() { StringBuilder b = new StringBuilder(); b.append(toString()); if (privKey != null && privKey instanceof BCECPrivateKey) { b.append(" priv:").append(Hex.toHexString(((BCECPrivateKey) privKey).getD().toByteArray())); } return b.toString(); } /** * Signs the given hash and returns the R and S components as BigIntegers and putData them in * ECDSASignature * * @param input to sign * @return ECDSASignature signature that contains the R and S components */ public ECDSASignature doSign(byte[] input) { if (input.length != 32) { throw new IllegalArgumentException("Expected 32 byte input to " + "ECDSA signature, not " + input.length); } // No decryption of private key required. if (privKey == null) { throw new MissingPrivateKeyException(); } if (privKey instanceof BCECPrivateKey) { ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())); ECPrivateKeyParameters privKeyParams = new ECPrivateKeyParameters (((BCECPrivateKey) privKey).getD(), CURVE); signer.init(true, privKeyParams); BigInteger[] components = signer.generateSignature(input); return new ECDSASignature(components[0], components[1]) .toCanonicalised(); } else { try { final Signature ecSig = ECSignatureFactory.getRawInstance (provider); ecSig.initSign(privKey); ecSig.update(input); final byte[] derSignature = ecSig.sign(); return ECDSASignature.decodeFromDER(derSignature) .toCanonicalised(); } catch (SignatureException | InvalidKeyException ex) { throw new RuntimeException("ECKey signing error", ex); } } } /** * Takes the keccak hash (32 bytes) of data and returns the ECDSA signature * * @param messageHash - * @return - * @throws IllegalStateException if this ECKey does not have the private part. */ public ECDSASignature sign(byte[] messageHash) { ECDSASignature sig = doSign(messageHash); // Now we have to work backwards to figure out the recId needed to // recover the signature. int recId = -1; byte[] thisKey = this.pub.getEncoded(/* compressed */ false); for (int i = 0; i < 4; i++) { byte[] k = ECKey.recoverPubBytesFromSignature(i, sig, messageHash); if (k != null && Arrays.equals(k, thisKey)) { recId = i; break; } } if (recId == -1) { throw new RuntimeException("Could not construct a recoverable key" + ". This should never happen."); } sig.v = (byte) (recId + 27); return sig; } public BigInteger keyAgreement(ECPoint otherParty) { if (privKey == null) { throw new MissingPrivateKeyException(); } else if (privKey instanceof BCECPrivateKey) { final ECDHBasicAgreement agreement = new ECDHBasicAgreement(); agreement.init(new ECPrivateKeyParameters(((BCECPrivateKey) privKey).getD(), CURVE)); return agreement.calculateAgreement(new ECPublicKeyParameters (otherParty, CURVE)); } else { try { final KeyAgreement agreement = ECKeyAgreement.getInstance (this.provider); agreement.init(this.privKey); agreement.doPhase( ECKeyFactory.getInstance(this.provider) .generatePublic(new ECPublicKeySpec (otherParty, CURVE_SPEC)), /* lastPhase */ true); return new BigInteger(1, agreement.generateSecret()); } catch (IllegalStateException | InvalidKeyException | InvalidKeySpecException ex) { throw new RuntimeException("ECDH key agreement failure", ex); } } } /** * Decrypt cipher by AES in SIC(also know as CTR) mode * * @param cipher -proper cipher * @return decrypted cipher, equal length to the cipher. * @deprecated should not use EC private scalar value as an AES key */ public byte[] decryptAES(byte[] cipher) { if (privKey == null) { throw new MissingPrivateKeyException(); } if (!(privKey instanceof BCECPrivateKey)) { throw new UnsupportedOperationException("Cannot use the private " + "key as an AES key"); } AESEngine engine = new AESEngine(); SICBlockCipher ctrEngine = new SICBlockCipher(engine); KeyParameter key = new KeyParameter(BigIntegers.asUnsignedByteArray(( (BCECPrivateKey) privKey).getD())); ParametersWithIV params = new ParametersWithIV(key, new byte[16]); ctrEngine.init(false, params); int i = 0; byte[] out = new byte[cipher.length]; while (i < cipher.length) { ctrEngine.processBlock(cipher, i, out, i); i += engine.getBlockSize(); if (cipher.length - i < engine.getBlockSize()) { break; } } // process left bytes if (cipher.length - i > 0) { byte[] tmpBlock = new byte[16]; System.arraycopy(cipher, i, tmpBlock, 0, cipher.length - i); ctrEngine.processBlock(tmpBlock, 0, tmpBlock, 0); System.arraycopy(tmpBlock, 0, out, i, cipher.length - i); } return out; } /** * 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 signature. * @return - */ public boolean verify(byte[] data, byte[] signature) { return ECKey.verify(data, signature, getPubKey()); } /** * Verifies the given R/S pair (signature) against a hash using the public key. * * @param sigHash - * @param signature - * @return - */ public boolean verify(byte[] sigHash, ECDSASignature signature) { return ECKey.verify(sigHash, signature, getPubKey()); } /** * Returns true if this pubkey is canonical, i.e. the correct length taking into account * compression. * * @return - */ public boolean isPubKeyCanonical() { return isPubKeyCanonical(pub.getEncoded(/* uncompressed */ false)); } /** * Returns a 32 byte array containing the private key, or null if the key is encrypted or public * only * * @return - */ @Nullable public byte[] getPrivKeyBytes() { if (privKey == null) { return null; } else if (privKey instanceof BCECPrivateKey) { return ByteUtil.bigIntegerToBytes(((BCECPrivateKey) privKey).getD(), 32); } else { return null; } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ECKey ecKey = (ECKey) o; if (privKey != null && !privKey.equals(ecKey.privKey)) { return false; } return pub == null || pub.equals(ecKey.pub); } @Override public int hashCode() { return Arrays.hashCode(getPubKey()); } public static class ECDSASignature { /** * The two components of the signature. */ public final BigInteger r, s; public byte v; /** * Constructs a signature with the given components. Does NOT automatically canonicalise the * signature. * * @param r - * @param s - */ public ECDSASignature(BigInteger r, BigInteger s) { this.r = r; this.s = s; } /** * t * * @return - */ private static ECDSASignature fromComponents(byte[] r, byte[] s) { return new ECDSASignature(new BigInteger(1, r), new BigInteger(1, s)); } /** * @param r - * @param s - * @param v - * @return - */ public static ECDSASignature fromComponents(byte[] r, byte[] s, byte v) { ECDSASignature signature = fromComponents(r, s); signature.v = v; return signature; } public static boolean validateComponents(BigInteger r, BigInteger s, byte v) { if (v != 27 && v != 28) { return false; } if (BIUtil.isLessThan(r, BigInteger.ONE)) { return false; } if (BIUtil.isLessThan(s, BigInteger.ONE)) { return false; } if (!BIUtil.isLessThan(r, SECP256K1N)) { return false; } return BIUtil.isLessThan(s, SECP256K1N); } public static ECDSASignature decodeFromDER(byte[] bytes) { ASN1InputStream decoder = null; try { decoder = new ASN1InputStream(bytes); DLSequence seq = (DLSequence) decoder.readObject(); if (seq == null) { throw new RuntimeException("Reached past end of ASN.1 " + "stream."); } ASN1Integer r, s; try { r = (ASN1Integer) seq.getObjectAt(0); s = (ASN1Integer) seq.getObjectAt(1); } catch (ClassCastException e) { throw new IllegalArgumentException(e); } // OpenSSL deviates from the DER spec by interpreting these // values as unsigned, though they should not be // Thus, we always use the positive versions. See: // http://r6.ca/blog/20111119T211504Z.html return new ECDSASignature(r.getPositiveValue(), s .getPositiveValue()); } catch (IOException e) { throw new RuntimeException(e); } finally { if (decoder != null) { try { decoder.close(); } catch (IOException x) { } } } } public boolean validateComponents() { return validateComponents(r, s, v); } public ECDSASignature toCanonicalised() { if (s.compareTo(HALF_CURVE_ORDER) > 0) { // The order of the curve is the number of valid points that // exist on that curve. If S is in the upper // half of the number of valid points, then bring it back to // the lower half. Otherwise, imagine that // N = 10 // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) // are valid solutions. // 10 - 8 == 2, giving us always the latter solution, // which is canonical. return new ECDSASignature(r, CURVE.getN().subtract(s)); } else { return this; } } /** * @return - */ public String toBase64() { byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 // bytes for S sigData[0] = v; System.arraycopy(ByteUtil.bigIntegerToBytes(this.r, 32), 0, sigData, 1, 32); System.arraycopy(ByteUtil.bigIntegerToBytes(this.s, 32), 0, sigData, 33, 32); return new String(Base64.encode(sigData), Charset.forName("UTF-8")); } public byte[] toByteArray() { final byte fixedV = this.v >= 27 ? (byte) (this.v - 27) : this.v; return ByteUtil.merge( ByteUtil.bigIntegerToBytes(this.r, 32), ByteUtil.bigIntegerToBytes(this.s, 32), new byte[]{fixedV}); } public String toHex() { return Hex.toHexString(toByteArray()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ECDSASignature signature = (ECDSASignature) o; if (!r.equals(signature.r)) { return false; } return s.equals(signature.s); } @Override public int hashCode() { int result = r.hashCode(); result = 31 * result + s.hashCode(); return result; } } @SuppressWarnings("serial") public static class MissingPrivateKeyException extends RuntimeException { } }