WIF Private Key

Wallet Import Format

A WIF (Wallet Import Format) private key is an address-style format for a private key.

It's used when exporting and importing private keys between bitcoin wallets.

It's mainly a Base58 encoding of the private key, but also includes some extra useful data and a checksum.

tool-6623f8761e932
Tool Icon

WIF Private Key

Convert between a raw private key and Wallet Import Format.

1 byte
Network
0 bytes
1 byte
Compressed
0 bytes

Base58 encoding of above data

0 characters

Never enter your private key in to a website, or use a private key generated by a website. Websites can easily save the private key and use it to steal your bitcoins.

0 secs

A WIF private key is just another way of representing your private key. If you have a WIF private key, you can always convert it back to a raw private key.

Never reveal your WIF private key. A WIF private key is not encrypted in any way, so you need to protect it as much as you would a raw private key.

Benefits

Why do we use WIF private keys?

The main benefit WIF is that it's a Base58 encoding of a private key, so it's shorter and easier to copy.

There are a few other benefits:

Encoding

Convert a private key to WIF

Diagram showing how to convert a private key to WIF.

Converting a raw private key to WIF is fairly straightforward:

  1. Start with a 32-byte hexadecimal private key.
  2. Add a version byte to the start. This indicates whether the private key is being used on mainnet or testnet:
    • 80 = mainnet
    • ef = testnet
  3. Add a compression byte to the end (optional). This indicates whether the private key is being used to create a compressed or uncompressed public key:
    • 01 = compressed public key (most common)
    • (no byte) = uncompressed public key
  4. Create a checksum from the above data and add it to the end.
    • A checksum in Bitcoin is the first 4 bytes of the Hash256 of some data.
  5. Convert all of the above data to Base58.

And there you have a WIF private key.

Code

# ---------
# Functions
# ---------

require 'digest'

# hash256 function (checksums use hash256)
def hash256(hex)
  binary = [hex].pack("H*")
  hash1 = Digest::SHA256.digest(binary)
  hash2 = Digest::SHA256.digest(hash1)
  result = hash2.unpack("H*")[0]
  return result
end

# checksum function
def checksum(hex)
  hash = hash256(hex) # Hash the data through SHA256 twice
  return hash[0...8]  # Return the first 4 bytes (8 characters)
end

# base58 encode function
def base58_encode(hex)
  @chars = %w[
      1 2 3 4 5 6 7 8 9
    A B C D E F G H   J K L M N   P Q R S T U V W X Y Z
    a b c d e f g h i j k   m n o p q r s t u v w x y z
]
  @base = @chars.length

  i = hex.to_i(16)
  buffer = String.new

  while i > 0
    remainder = i % @base
    i = i / @base
    buffer = @chars[remainder] + buffer
  end

  # add '1's to the start based on number of leading bytes of zeros
  leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2

  ("1"*leading_zero_bytes) + buffer
end

# ----------
# WIF Encode
# ----------

# 1. start with a 32-byte hexadecimal private key
privatekey = "ef235aacf90d9f4aadd8c92e4b2562e1d9eb97f0df9ba3b508258739cb013db2" # example, do not use

# 2. add prefix (80 = mainet, ef = testnet)
data = "80" + privatekey

# 3. add compression byte (optional)
data = data + "01"

# 4. add checksum
data = data + checksum(data)

# 5. base58 encode
wif = base58_encode(data)

# result
puts wif #=> L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6

On mainnet a WIF should start with a K, L, or 5. On testnet it should start with a c or a 9.

Decoding

Convert from WIF to a private key

Diagram showing how to convert a WIF to a private key.

It's easy enough to extract a raw private key from WIF:

  1. Start with the 51 or 52 character WIF private key.
  2. Decode it from Base58.
    • This will give you 37 or 38 hexadecimal bytes.
  3. The private key is contained within bytes 1-33 (i.e. the 32 bytes after the first byte).

If you need to you can always extract the extra data from the WIF private key:

Code

# ---------
# Functions
# ---------

# base58 decode function
def base58_decode(base58_string)

  # base58 characters
  base58_chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

  # set starting integer value
  integer = 0

  # run through each character in the string from right to left
  base58_string.reverse.each_char.with_index do |char, index|
  
    # get the position of the character in the list of base58 characters
    char_index = base58_chars.index(char)
    
    # check its a valid base58 character
    if !char_index
     puts "Not a valid Base58 character: #{char}"
     exit
    end
    
    # multiply the position of the character by a power of 58 (increasing for the index of each character)
    integer += char_index * (58**index)
  end
  
  # convert the integer to a hexadecimal string
  hexadecimal = integer.to_s(16)
  
  # pad out to make sure it's an even number of bytes
  if hexadecimal.bytesize.odd?
    hexdecimal = "0" + hexadecimal
  end
  
  # ignore empty base58 strings
  if hexadecimal == "00"
    hexadecimal = ""
  end
  
  # count the number of leading 1's
  leading_zero_bytes = (base58_string.match(/^([1]+)/) ? $1 : '').size
  
  # convert leading 1's to leading 00's
  if leading_zero_bytes > 0
    hexadecimal = ("00" * leading_zero_bytes) + hexadecimal
  end
  
  # return hexadecimal bytes
  return hexadecimal
end

# ----------
# WIF Decode
# ----------

# 1. start with a wif private key
wif = "L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6" # example, do not use

# 2. base58 decode
data = base58_decode(wif)

# 3. extract the private key (bytes 1 to 33)
privatekey = data[2...66]

# result
puts privatekey #=> ef235aacf90d9f4aadd8c92e4b2562e1d9eb97f0df9ba3b508258739cb013db2

Most of the time you're working with compressed public keys, and you already know if you're using it on mainnet or testnet, so a quick and dirty way to get the raw private key is to just decode WIF from Base58 and take bytes 1 to 33.

Usage

Where are WIF private keys used in Bitcoin?

Screenshot of the private key sweep tool in Sparrow Wallet.
Sparrow Wallet requires you to use WIF when importing private keys.

As mentioned, WIF is used when you're exporting or importing private keys between wallets.

Some examples of popular wallets that use WIF are:

From my experience it's more common for wallets to require you to work with WIF private keys as opposed to allowing you to import/export raw private keys.

History

When were WIF private keys introduced in Bitcoin?

Pieter Wuille created the WIF format in 2011 for importing and exporting private keys via Bitcoin Core.

https://github.com/bitcoin/bitcoin/pull/574

Thanks to Murch and Ava Chow for the help with the origin of WIF private keys.

Summary

WIF is just designed to be a more user-friendly encoding of a private key.

You can spot a WIF private key because it's a Base58 string and starts with a K or L. On the odd occasion it will start with a 5 (if you're working with uncompressed public keys for some reason). For example:

These are static examples of WIF private keys. Do not use them.

So if you're working on mainnet with compressed public keys like a normal person, look for the K or L at the start.

From my experience it can be a bit annoying to have to convert between raw private key and WIF when you want to import a private key in to a wallet, but it's common practice for wallets to work with WIF instead of raw private keys, so you might as well get used to it.

If you're working with raw private keys locally (and occasionally want to import/export them), it's a good idea to write your own WIF encoder/decoder. The last thing you want to do is trust a website or tool to handle your private keys, and it's not hard to create a tool that converts between WIF and a raw private key yourself.

It will save time and stress later on. Trust me.

Because as that famous old saying goes, "Not Your Own WIF Tool, Not Your Coins".

Or something like that.