WIF private key
WIF private key format: Wallet Import Format writes a Bitcoin private key in Base58, with a network byte and a checksum. Learn to encode and decode it.
By Dimitar Zurnov · Updated 4 Nov 2026
Adapted from Learn Me a Bitcoin by Greg Walker.
WIF, short for Wallet Import Format, is a way to write a private key as a Base58 string. It adds a byte that marks the network, an optional byte that marks compressed keys, and a checksum that catches typing mistakes. Mainnet WIF strings start with 5, K or L.
A WIF string is not a different key. The same 32 bytes can be written as hex or as WIF, and you can always convert back. Because a WIF string holds the key itself, it needs the same care as the private key it encodes.
Why use WIF instead of hex?
WIF has three practical advantages over a 64-digit hex key:
- It is shorter, 51 or 52 characters, so it is easier to copy.
- It starts with a recognizable character, so you can tell a private key from a random string.
- Its checksum catches a mistyped character when you import it. It cannot repair the mistake, but it stops a bad string from being used.
The compression flag adds a fourth benefit. It tells a wallet which public key format the key was meant for, so the wallet knows which address to look for.
What are the fields of a WIF string?
| Field | Size | Value |
|---|---|---|
| Version byte | 1 byte | 80 on mainnet, ef on testnet |
| Private key | 32 bytes | The raw key, unchanged |
| Compression flag | 1 byte, optional | 01 for a compressed public key. Absent for an uncompressed one. |
| Checksum | 4 bytes | The first 4 bytes of hash256 of all the fields above |
The bytes are then encoded in Base58, an alphabet that leaves out 0, O, I and l so that characters are harder to confuse.
How do you encode a private key as WIF?
- Start with the 32-byte private key in hex.
- Put the version byte in front:
80for mainnet,effor testnet. - Add
01at the end if the key is for a compressed public key. Most keys in use today are compressed. - Hash the data twice with SHA-256. Keep the first 4 bytes and append them as the checksum.
- Base58 encode the whole result.
This page uses its own example key, ef235aacf90d9f4aadd8c92e4b2562e1d9eb97f0df9ba3b508258739cb013db2. It is not the key from the private key page, which starts dbf34ebb, so the two examples do not mix. Here is this key worked through the steps. The bytes before the checksum are 80, then the key, then 01. The checksum is 66557e53, and the WIF string is L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6. The key is published, so anyone can spend coins sent to it. Do not use it.
This Python function does the same steps. It needs no library beyond the standard one.
import hashlib
BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def base58(data: bytes) -> str:
n = int.from_bytes(data, "big")
out = ""
while n > 0:
n, r = divmod(n, 58)
out = BASE58[r] + out
pad = len(data) - len(data.lstrip(b"\x00")) # each leading zero byte becomes a "1"
return "1" * pad + out
def hash256(data: bytes) -> bytes:
return hashlib.sha256(hashlib.sha256(data).digest()).digest()
def wif_encode(key_hex: str, testnet=False, compressed=True) -> str:
data = (b"\xef" if testnet else b"\x80") + bytes.fromhex(key_hex)
if compressed:
data += b"\x01"
data += hash256(data)[:4]
return base58(data)
print(wif_encode("ef235aacf90d9f4aadd8c92e4b2562e1d9eb97f0df9ba3b508258739cb013db2"))
# L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6
Check any encoder against this example before you trust it with a real key.
How do you decode a WIF string?
Decoding runs the steps in reverse:
- Base58 decode the string. A compressed WIF gives 38 bytes, and an uncompressed one gives 37.
- Hash everything except the last 4 bytes, and compare the first 4 bytes of that hash with the checksum. A mismatch means a typo.
- Read the version byte to find the network.
- Take bytes 1 to 32, counting from zero, as the private key.
- If byte 33 is present, it is the compression flag.
The same logic in Python:
def wif_decode(wif: str) -> str:
n = 0
for c in wif:
n = n * 58 + BASE58.index(c)
raw = n.to_bytes((n.bit_length() + 7) // 8, "big")
payload, checksum = raw[:-4], raw[-4:]
if hash256(payload)[:4] != checksum:
raise ValueError("checksum mismatch: the string has a typo")
return payload[1:33].hex() # bytes 1 to 32: the private key
Run it on the example string and you get the example key back.
Which first character shows the network and key type?
The first character changes with the version byte and the compression flag. These are the four cases:
| Starts with | Network | Public key |
|---|---|---|
5 |
Mainnet | Uncompressed |
K or L |
Mainnet | Compressed |
9 |
Testnet | Uncompressed |
c |
Testnet | Compressed |
Most mainnet keys you will meet start with K or L, because almost every wallet uses compressed public keys today.
Where is WIF used?
WIF is mainly used to move a single private key between programs. Bitcoin Core's importprivkey RPC takes a WIF string, and dumpprivkey returns one. The dumpprivkey command arrived in late 2011, in pull request 574. Many wallets ask for WIF when you import a key, rather than raw hex.
WIF is not encrypted. BIP 38 defines a passphrase-protected form that starts with 6P. A plain WIF string is not protected that way, so treat it as the key itself.
How do you keep a WIF string safe?
- A WIF string is not encrypted. Anyone who sees it can spend the coins, so guard it like the raw key.
- Do not paste it into a website or app you do not control. A site that asks for one, or generates one for you, can keep a copy.
- Check any tool against known examples before you use it, or write your own encoder as shown above.
The format is documented in the Bitcoin Wiki's Wallet import format page, which describes the prefixes and the flag used here.
The next page, public key, shows how the same private key gives a public key.
In this section: Keys and addresses
Something look wrong? Tell us