Client and backend. This class is shared: the same code is compiled into your app and into your server, so everything on this page works in both.
public final class Secp256k1
- Object
- Secp256k1
ECDSA over the secp256k1 curve, the signature scheme of Bitcoin and Ethereum, written in portable Java so a wallet can sign on the device and a server can verify or sign with the same class.
Keys are raw bytes: a private key is 32 big-endian bytes in [1, n-1], a
public key is the SEC 1 encoding of the point, either compressed (33
bytes, 02/03 then x) or uncompressed (65 bytes, 04 then x then
y). Every method accepts a public key in either form.
Signing an Ethereum transaction
byte[] priv = Hash.fromHex("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318");
// the address is the last 20 bytes of keccak256(x || y)
byte[] pub = Secp256k1.publicKey(priv, false); // 04 || x || y
byte[] xy = new byte[64];
System.arraycopy(pub, 1, xy, 0, 64);
byte[] digest = Hash.keccak256(xy);
byte[] address = new byte[20];
System.arraycopy(digest, 12, address, 0, 20); // 2c7536e3605d9c16a7a3d7b1898e529396a65c23
// sign the keccak256 of the RLP-encoded transaction
byte[] rlp = Hash.fromHex("02c0"); // your encoded transaction
Secp256k1.EcdsaSignature sig = Secp256k1.sign(Hash.keccak256(rlp), priv);
byte[] r = sig.r();
byte[] s = sig.s();
int v = 27 + sig.recoveryId(); // or chainId * 2 + 35 + recoveryId
Signatures are deterministic (RFC 6979, HMAC-SHA256) and always low-S, as
Bitcoin’s standardness rules and Ethereum’s EIP-2 require, and they carry
the recovery id Ethereum derives the signer’s address from. verify
rejects a high-S signature the way libsecp256k1 does; recoverPublicKey
accepts either, the way the EVM’s ecrecover does. A signature from
another library can be lowered with EcdsaSignature.normalize.
Constant time
Everything that touches a private key or a nonce – key derivation, signing, the tweak used by BIP32 derivation – runs in constant time: the field and scalar arithmetic uses fixed 16-bit limbs with no data-dependent branches, the curve arithmetic uses complete addition formulas that have no special case for doubling or the point at infinity, and the scalar multiplication is a fixed-window ladder whose table lookup reads every entry. Verification and recovery handle public data only and share the same code. The implementation is independent of libsecp256k1 and depends on nothing but this package and the core JDK subset, so it runs identically on every port and on the backend.
Nested types
class Secp256k1.EcdsaSignature | An ECDSA signature: r and s as 32-byte big-endian integers, and the recovery id a signer knows and a verifier does not need. |
Fields
public static final int PRIVATE_KEY_LENGTH = 32 | Length in bytes of a private key. |
public static final int HASH_LENGTH = 32 | Length in bytes of the message hash a signature covers. |
public static final int COMPRESSED_PUBLIC_KEY_LENGTH = 33 | Length in bytes of a compressed public key (02/03 then x). |
public static final int UNCOMPRESSED_PUBLIC_KEY_LENGTH = 65 | Length in bytes of an uncompressed public key (04 then x then y). |
Methods
Inherited methods
Field details
PRIVATE_KEY_LENGTH
public static final int PRIVATE_KEY_LENGTH = 32HASH_LENGTH
public static final int HASH_LENGTH = 32COMPRESSED_PUBLIC_KEY_LENGTH
public static final int COMPRESSED_PUBLIC_KEY_LENGTH = 3302/03 then x).UNCOMPRESSED_PUBLIC_KEY_LENGTH
public static final int UNCOMPRESSED_PUBLIC_KEY_LENGTH = 6504 then x then y).Method details
isValidPrivateKey
public static boolean isValidPrivateKey(byte[] privateKey)[1, n-1].publicKey
public static byte[] publicKey(byte[] privateKey, boolean compressed)Parameters
privateKeybyte[]- 32 bytes in
[1, n-1] compressedbooleantruefor the 33-byte SEC 1 form,falsefor the 65-byte form Ethereum derives an address from
Throws
CryptoException- if the private key is not valid
isValidPublicKey
public static boolean isValidPublicKey(byte[] publicKey)convertPublicKey
public static byte[] convertPublicKey(byte[] publicKey, boolean compressed)Throws
CryptoException- if the key is not a point on the curve
privateKeyTweakAdd
public static byte[] privateKeyTweakAdd(byte[] privateKey, byte[] tweak)Parameters
privateKeybyte[]- 32 bytes in
[1, n-1] tweakbyte[]- 32 bytes, less than
n
Returns
(privateKey + tweak) mod n, 32 bytesThrows
CryptoException- if either input is out of range or the sum is zero
publicKeyTweakAdd
public static byte[] publicKeyTweakAdd(byte[] publicKey, byte[] tweak, boolean compressed)tweak * G to a public key: the public half of a BIP32 child
derivation, matching privateKeyTweakAdd on the private key.Throws
CryptoException- if the key is not on the curve, the tweak is not
less than
n, or the result is the point at infinity
publicKeyAdd
public static byte[] publicKeyAdd(byte[] a, byte[] b, boolean compressed)Throws
CryptoException- if either key is not on the curve or the sum is the point at infinity
sign
public static Secp256k1.EcdsaSignature sign(byte[] hash, byte[] privateKey)Parameters
hashbyte[]- 32 bytes, the digest of the message (Keccak-256 of the RLP-encoded transaction on Ethereum, double SHA-256 on Bitcoin)
privateKeybyte[]- 32 bytes in
[1, n-1]
Throws
CryptoException- if the hash is not 32 bytes or the key is not valid
verify
public static boolean verify(byte[] hash, Secp256k1.EcdsaSignature signature, byte[] publicKey)s is in
the upper half of the range is rejected, as libsecp256k1 and the
Bitcoin and Ethereum networks reject it; see
EcdsaSignature.normalize.Returns
true if the signature was made over hash by the holder of the
private key behind publicKeyThrows
CryptoException- if the hash is not 32 bytes or the public key is not a point on the curve
recoverPublicKey
public static byte[] recoverPublicKey(byte[] hash, Secp256k1.EcdsaSignature signature, boolean compressed)verify this accepts a high-S signature,
as the EVM’s ecrecover does.Parameters
hashbyte[]- the 32 bytes that were signed
signatureSecp256k1.EcdsaSignature- a signature whose recovery id is known (0 to 3)
compressedboolean- the form to return the key in
Returns
null if no key could have produced the signatureThrows
CryptoException- if the hash is not 32 bytes or the signature has no recovery id