/** * @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); } }
/** * @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); } }
/** * 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 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; }
@Test public void testVerifySignature2() throws SignatureException { BigInteger r = new BigInteger("c52c114d4f5a3ba904a9b3036e5e118fe0dbb987fe3955da20f2cd8f6c21ab9c", 16); BigInteger s = new BigInteger("6ba4c2874299a55ad947dbc98a25ee895aabf6b625c26c435e84bfd70edf2f69", 16); ECDSASignature sig = ECDSASignature.fromComponents(r.toByteArray(), s.toByteArray(), (byte) 0x1b); byte[] rawtx = Hex.decode("f82804881bc16d674ec8000094cd2a3d9f938e13cd947ec05abc7fe734df8dd8268609184e72a0006480"); byte[] rawHash = HashUtil.sha3(rawtx); byte[] address = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826"); ECKey key = ECKey.signatureToKey(rawHash, sig); assertEquals(key, ECKey.signatureToKey(rawHash, sig.toBase64())); assertEquals(key, ECKey.recoverFromSignature(0, sig, rawHash)); assertArrayEquals(key.getPubKey(), ECKey.recoverPubBytesFromSignature(0, sig, rawHash)); assertArrayEquals(address, key.getAddress()); assertArrayEquals(address, ECKey.signatureToAddress(rawHash, sig)); assertArrayEquals(address, ECKey.signatureToAddress(rawHash, sig.toBase64())); assertArrayEquals(address, ECKey.recoverAddressFromSignature(0, sig, rawHash)); assertTrue(key.verify(rawHash, sig)); }