Crypto Signature Verifier

Paste a message, a signature and (optionally) the address that claims to have signed it. The tool recovers the Ethereum signer, verifies Solana ed25519 and Bitcoin BIP-137 signatures, and shows every intermediate hash. Nothing is uploaded and no private key is ever involved.

Examples:

Computed in your browser with JavaScript. Addresses are never uploaded or stored.

What is a signature verifier?

A signature verifier takes a message, a digital signature and a public identity (an address or public key) and tells you whether that identity really produced the signature over exactly that message. On Ethereum the check is done by recovery: secp256k1 lets you compute the signer's public key from the signature and the message hash, hash it to an address, and compare. On Solana (ed25519) and for Bitcoin's SegWit and legacy message formats the same idea applies, with different hashing rules. This page implements all four conventions used by wallets today, in the browser, from public data only.

Supported signature types

TypeStandardDigest that is signedCurveOutput
Ethereum personal_signEIP-191 (version 0x45)keccak256("\x19Ethereum Signed Message:\n" + length + message)secp256k1Recovered address, match badge, message hash
Ethereum typed dataEIP-712 (eth_signTypedData_v4)keccak256(0x1901 ‖ domainSeparator ‖ hashStruct(message))secp256k1Domain separator, struct hash, digest, recovered signer
Solana signMessageRFC 8032 Ed25519SHA-512 inside the EdDSA equation, no prefixed25519Valid / invalid
Bitcoin signed messageBIP-137 (Bitcoin Core signmessage)SHA-256d("\x18Bitcoin Signed Message:\n" + varint(len) + message)secp256k1Recovered key, derived address, match

How Ethereum recovery works

An ECDSA signature is the pair (r, s). Given the 32-byte digest and the recovery id v, there are at most two candidate public keys; v selects one. The tool computes the digest according to the selected standard, recovers the 64-byte public key, takes the last 20 bytes of its Keccak-256 hash to get the address, and compares it with the expected address if you gave one. Because the address is derived rather than looked up, a match proves possession of the key at signing time; no on-chain data is needed.

Worked example: EIP-712 Mail

Click "EIP-712 Mail" above. The typed data is the example from the EIP-712 specification: domain Ether Mail version 1 on chain 1 with verifying contract 0xcccc…cccc, and a Mail struct from Cow to Bob with contents "Hello, Bob!". The tool shows the domain separator 0xf2cee375…, the struct hash 0xc52c0ee5… and the final digest 0xbe609aee…, exactly the values printed in the specification's reference implementation, and then recovers the signer of the attached signature. Change one character of "Hello, Bob!" and the recovered address changes completely, which is the whole point of signing structured data.

Reading a Bitcoin message signature

The first byte of the base64 signature encodes both the recovery id and the address type the signer intended: 27–30 for P2PKH with an uncompressed key, 31–34 for P2PKH with a compressed key, 35–38 for P2SH-wrapped SegWit and 39–42 for native SegWit (bc1q). The tool recovers the key, derives all four address forms, and reports which one matches the address you supplied. Electrum and several other wallets sign bc1q addresses with the 31–34 header; that case is accepted and flagged with a note rather than rejected. Testnet addresses (m…, n…, 2…, tb1q…) are recognised from their prefix.

Common uses

Accepted input formats

Related: compute Keccak-256 of a message yourself with the hash tool, fix an address checksum with the EIP-55 converter, or decode the transaction a signature was used in with the Ethereum transaction decoder.

Frequently asked questions

What is the difference between personal_sign and eth_signTypedData?

personal_sign (EIP-191) hashes the raw message with the prefix \x19Ethereum Signed Message:\n plus its length; the wallet shows the text but cannot explain it. EIP-712 hashes a typed structure together with a domain (name, version, chainId, verifyingContract), so wallets can render each field and a signature for one dApp or chain cannot be replayed on another. The recovery math is the same; only the digest differs.

Why does my recovered address not match?

Almost always because the message bytes differ: a trailing newline, Windows line endings, a different Unicode normalisation, or the text was signed as hex bytes rather than UTF-8. For EIP-712, a different chainId, verifyingContract or type definition changes the domain separator and therefore the signer. The signature itself is rarely wrong; compare the hashes shown here with what your code computed.

What does the v value mean?

The last byte of a 65-byte Ethereum signature is the recovery id: 27 or 28 in the original Ethereum encoding, 0 or 1 in newer libraries, and in EIP-2098 compact signatures it is folded into the top bit of s. All forms are accepted and normalised before recovery.

Does Solana verification need the RPC or the wallet?

No. Solana signMessage produces a plain ed25519 signature over the raw bytes, with no prefix, so verification only needs the message, the 64-byte signature and the 32-byte public key (the wallet address). This page implements RFC 8032 verification in JavaScript.

Which Bitcoin signature formats are supported?

The classic 65-byte base64 format produced by Bitcoin Core signmessage, Electrum, Sparrow and most hardware wallets (BIP-137): header byte 27–42, then r and s. Legacy, nested SegWit and native SegWit addresses are handled, including Electrum's habit of using the legacy header for bc1q addresses. BIP-322 and Taproot (bc1p) signatures use a different scheme and are not supported.

Can this tool sign anything?

No. Signing requires a private key, and this site never generates, imports or stores keys. It only verifies signatures from public data: the message, the signature and the signer's address or public key.

Is the signature sent to a server?

No. All hashing (Keccak-256, SHA-256, SHA-512) and curve arithmetic (secp256k1 recovery, ed25519 point operations) run in your browser. The page makes no network requests after loading.