public DSTU4145ECBinary(ECDomainParameters params) { if (!(params.getCurve() instanceof ECCurve.F2m)) { throw new IllegalArgumentException("only binary domain is possible"); } // We always use big-endian in parameter encoding ECCurve.F2m curve = (ECCurve.F2m)params.getCurve(); f = new DSTU4145BinaryField(curve.getM(), curve.getK1(), curve.getK2(), curve.getK3()); a = new ASN1Integer(curve.getA().toBigInteger()); X9IntegerConverter converter = new X9IntegerConverter(); b = new DEROctetString(converter.integerToBytes(curve.getB().toBigInteger(), converter.getByteLength(curve))); n = new ASN1Integer(params.getN()); bp = new DEROctetString(DSTU4145PointEncoder.encodePoint(params.getG())); }
/** Decompress a compressed public key (x co-ord and low-bit of y-coord). */ 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 void populateFromPubKeyInfo(SubjectPublicKeyInfo info) { X962Parameters params = new X962Parameters((ASN1Primitive)info.getAlgorithm().getParameters()); ECCurve curve = EC5Util.getCurve(configuration, params); ecSpec = EC5Util.convertToSpec(params, curve); DERBitString bits = info.getPublicKeyData(); byte[] data = bits.getBytes(); ASN1OctetString key = new DEROctetString(data); // // extra octet string - one of our old certs... // if (data[0] == 0x04 && data[1] == data.length - 2 && (data[2] == 0x02 || data[2] == 0x03)) { int qLength = new X9IntegerConverter().getByteLength(curve); if (qLength >= data.length - 3) { try { key = (ASN1OctetString) ASN1Primitive.fromByteArray(data); } catch (IOException ex) { throw new IllegalArgumentException("error recovering public key"); } } } X9ECPoint derQ = new X9ECPoint(curve, key); this.q = derQ.getPoint(); }
/** * Recover the public key that corresponds to the private key, which signed this message. */ public static byte[] recoverPublicKey(byte[] sigR, byte[] sigS, byte[] sigV, byte[] message) { ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(SECP256K1); BigInteger pointN = spec.getN(); try { BigInteger pointX = new BigInteger(1, sigR); X9IntegerConverter x9 = new X9IntegerConverter(); byte[] compEnc = x9.integerToBytes(pointX, 1 + x9.getByteLength(spec.getCurve())); compEnc[0] = (byte) ((sigV[0] & 1) == 1 ? 0x03 : 0x02); ECPoint pointR = spec.getCurve().decodePoint(compEnc); if (!pointR.multiply(pointN).isInfinity()) { return new byte[0]; } BigInteger pointE = new BigInteger(1, message); BigInteger pointEInv = BigInteger.ZERO.subtract(pointE).mod(pointN); BigInteger pointRInv = new BigInteger(1, sigR).modInverse(pointN); BigInteger srInv = pointRInv.multiply(new BigInteger(1, sigS)).mod(pointN); BigInteger pointEInvRInv = pointRInv.multiply(pointEInv).mod(pointN); ECPoint pointQ = ECAlgorithms.sumOfTwoMultiplies(spec.getG(), pointEInvRInv, pointR, srInv); return pointQ.getEncoded(false); } catch (Exception e) { LOGGER.warn("Error recovering public key from message"); } return new byte[0]; }
/** * Calculates the sent address of an EthereumTransaction. Note this can be a costly operation to calculate. . This requires that you have Bouncy castle as a dependency in your project * * * @param eTrans transaction * @return sent address as byte array */ public static byte[] getSendAddress(EthereumTransaction eTrans) { // init, maybe we move this out to save time X9ECParameters params = SECNamedCurves.getByName("secp256k1"); ECDomainParameters CURVE=new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); // needed for getSentAddress // transaction hash without signature data byte[] transactionHash = EthereumUtil.getTransactionHashWithoutSignature(eTrans); // signature to address BigInteger bR = new BigInteger(1,eTrans.getSig_r()); BigInteger bS = new BigInteger(1,eTrans.getSig_s()); // calculate v for signature byte v =(byte) (eTrans.getSig_v()[0]); if (!((v == EthereumUtil.LOWER_REAL_V) || (v== (LOWER_REAL_V+1)))) { v = EthereumUtil.LOWER_REAL_V; if (((int)v%2 == 0)) { v = (byte) (v+0x01); } } boolean compressedKey= false; // the following lines are inspired from ECKey.java of EthereumJ, but adapted to the hadoopcryptoledger context if (v < 27 || v > 34) { throw new RuntimeException("Header out of range"); } if (v>=31) { compressedKey = true; v -=4; } int receiverId = v - 27; BigInteger n = CURVE.getN(); BigInteger i = BigInteger.valueOf((long) receiverId / 2); BigInteger x = bR.add(i.multiply(n)); ECCurve.Fp curve = (ECCurve.Fp) CURVE.getCurve(); BigInteger prime = curve.getQ(); if (x.compareTo(prime) >= 0) { return null; } // decompress Key X9IntegerConverter x9 = new X9IntegerConverter(); byte[] compEnc = x9.integerToBytes(x, 1 + x9.getByteLength(CURVE.getCurve())); boolean yBit=(receiverId & 1) == 1; compEnc[0] = (byte)(yBit ? 0x03 : 0x02); ECPoint R = CURVE.getCurve().decodePoint(compEnc); if (!R.multiply(n).isInfinity()) { return null; } BigInteger e = new BigInteger(1,transactionHash); BigInteger eInv = BigInteger.ZERO.subtract(e).mod(n); BigInteger rInv = bR.modInverse(n); BigInteger srInv = rInv.multiply(bS).mod(n); BigInteger eInvrInv = rInv.multiply(eInv).mod(n); ECPoint.Fp q = (ECPoint.Fp) ECAlgorithms.sumOfTwoMultiplies(CURVE.getG(), eInvrInv, R, srInv); byte[] pubKey=q.getEncoded(false); // now we need to convert the public key into an ethereum sent address which is the last 20 bytes of 32 byte KECCAK-256 Hash of the key. Keccak.Digest256 digest256 = new Keccak.Digest256(); digest256.update(pubKey,1,pubKey.length-1); byte[] kcck = digest256.digest(); return Arrays.copyOfRange(kcck,12,kcck.length); }
/** * Determine the recovery ID for the given signature and public key. * * <p>Any signed message can resolve to one of two public keys due to the nature ECDSA. The * recovery ID provides information about which one it is, allowing confirmation that the message * was signed by a specific key.</p> */ public static byte getRecoveryId(byte[] sigR, byte[] sigS, byte[] message, byte[] publicKey) { ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(SECP256K1); BigInteger pointN = spec.getN(); for (int recoveryId = 0; recoveryId < 2; recoveryId++) { try { BigInteger pointX = new BigInteger(1, sigR); X9IntegerConverter x9 = new X9IntegerConverter(); byte[] compEnc = x9.integerToBytes(pointX, 1 + x9.getByteLength(spec.getCurve())); compEnc[0] = (byte) ((recoveryId & 1) == 1 ? 0x03 : 0x02); ECPoint pointR = spec.getCurve().decodePoint(compEnc); if (!pointR.multiply(pointN).isInfinity()) { continue; } BigInteger pointE = new BigInteger(1, message); BigInteger pointEInv = BigInteger.ZERO.subtract(pointE).mod(pointN); BigInteger pointRInv = new BigInteger(1, sigR).modInverse(pointN); BigInteger srInv = pointRInv.multiply(new BigInteger(1, sigS)).mod(pointN); BigInteger pointEInvRInv = pointRInv.multiply(pointEInv).mod(pointN); ECPoint pointQ = ECAlgorithms.sumOfTwoMultiplies(spec.getG(), pointEInvRInv, pointR, srInv); byte[] pointQBytes = pointQ.getEncoded(false); boolean matchedKeys = true; for (int j = 0; j < publicKey.length; j++) { if (pointQBytes[j] != publicKey[j]) { matchedKeys = false; break; } } if (!matchedKeys) { continue; } return (byte) (0xFF & recoveryId); } catch (Exception e) { LOGGER.error(null, e); } } return (byte) 0xFF; }