btc-keygen

offline bitcoin key generator

A minimal Rust CLI that generates a Bitcoin private key on an air-gapped machine. No wallet. No network. No trust required.

Why generate keys offline

When you use an exchange or an online wallet, someone else holds your private keys. If they get hacked, go bankrupt, or decide to freeze your account, your Bitcoin is gone. This has happened repeatedly throughout Bitcoin's history.

The Bitcoin community has a saying: not your keys, not your coins. If you don't personally control the private key, you don't truly own the Bitcoin.

But generating keys on a computer connected to the internet is also risky. Malware, keyloggers, clipboard hijackers, and compromised software can steal your private key the moment it's created, before you even know it exists.

The safest approach is simple: generate your key on a computer that has never been and will never be connected to the internet. Even if the machine were compromised, it would have no way to send your key to an attacker.

That's what this tool is for.

How cold storage works

1air-gap a pc 2run btc-keygen 3write down keys 4store safely

1. Get an air-gapped machine

Use any computer that is disconnected from the internet. An old laptop with Wi-Fi disabled works well. A Raspberry Pi is another good option. The key requirement: no network connection, ever, while the machine has access to your private key.

2. Copy btc-keygen onto the machine

Download the binary on a separate, internet-connected computer. Verify the checksum. Copy it to a USB drive. Plug the USB drive into the air-gapped machine.

3. Run the tool

Open a terminal and run btc-keygen generate. The tool prints two things:

  • A Bitcoin address (starts with bc1q) — your receiving address. Like a bank account number: give it to people so they can send you Bitcoin. Safe to share publicly.
  • A private key in WIF format (starts with K or L) — "Wallet Import Format." Think of it as the password to your Bitcoin. Anyone who has this string can spend your funds. Keep it absolutely secret.

4. Write it down

Write both the address and the private key on paper, or stamp them into metal. Do not save them to a file. The tool does not save anything — once you close the terminal, the key exists only where you wrote it down.

5. Store it securely

Put the paper or metal in a safe, a safety deposit box, or another secure location. This is your cold storage. To receive Bitcoin, give someone the address. To spend it later, import the private key into a wallet.

Critical. The tool generates a fresh key every time you run it. If you lose your written copy, there is no way to recover the private key. No one can help you — not the tool, not the developers, not anyone. Write it down carefully and store it safely.

Using your address and private key

Receiving Bitcoin

Share your address (the bc1q… string) with the sender. You can share it by text, email, or any other channel — the address is public information and cannot be used to steal your Bitcoin.

Check whether Bitcoin has arrived using any public block explorer (for example, search your address on mempool.space or blockchair.com). You do not need your private key to check your balance — only to spend.

Spending Bitcoin

When you want to spend the Bitcoin stored at your address, you need to import your WIF private key into a wallet application:

  1. Install a Bitcoin wallet that supports WIF import — for example Electrum, BlueWallet, or Sparrow Wallet.
  2. Look for an option called Import, Sweep, or Import Private Key. Exact wording varies by wallet.
  3. Enter or scan the WIF string — the one starting with K or L.
  4. The wallet will recognize the key, find the associated address, and show your balance.
  5. You can now send Bitcoin from that address.

Important. When you import or sweep a private key, you are exposing it to that device. Only do this on a trusted device. After spending, if you want to continue using cold storage, generate a new keypair for the remaining funds rather than reusing the same key.

Import vs sweep

  • Import — adds the key to the wallet. The wallet now controls that address directly. Funds stay at the same address.
  • Sweep — moves all funds from the imported key into a new address controlled by the wallet. The old address ends up empty. Generally the safer option because the private key is used only once.

Security design

This tool was designed as if every run could protect someone's life savings. Here is what is under the hood, in plain language:

Randomness from your OS
Your private key is generated using your operating system's cryptographic random number generator — the same source used by SSH, TLS, and disk encryption.
Bitcoin Core's own math library
The elliptic curve operations use libsecp256k1, the same code that powers Bitcoin Core. The most reviewed implementation in the Bitcoin ecosystem.
Memory is wiped after use
The private key bytes are overwritten in memory the moment the tool finishes, so the key does not linger in RAM.
Zero network code
The tool contains no networking code at all. It cannot connect to the internet, phone home, or leak your keys over any channel.
Minimal and auditable
The entire codebase is under 500 lines of Rust with 6 dependencies. A competent developer can read and verify the whole program in an afternoon.
Tested against known vectors
Every component is tested against published Bitcoin test vectors, ensuring the keys and addresses are valid on the real Bitcoin network.

For the full technical threat model, security assumptions, and dependency analysis, see the design documentation in the repository.

Download

Pre-built binaries are available for every major platform. Each binary is a single file with no dependencies — download it, verify the checksum, and run it.

Always verify the checksum. Download the SHA256SUMS.txt file from the release and compare it against the binary you downloaded. This ensures the file has not been tampered with.

Download the latest release ↗

Linux (x86_64)

curl -LO https://github.com/aguimaraes/btc-keygen/releases/latest/download/btc-keygen-linux-x86_64
sha256sum btc-keygen-linux-x86_64
chmod +x btc-keygen-linux-x86_64
./btc-keygen-linux-x86_64 generate

Linux (ARM64 / Raspberry Pi)

curl -LO https://github.com/aguimaraes/btc-keygen/releases/latest/download/btc-keygen-linux-aarch64
sha256sum btc-keygen-linux-aarch64
chmod +x btc-keygen-linux-aarch64
./btc-keygen-linux-aarch64 generate

macOS (Apple Silicon)

curl -LO https://github.com/aguimaraes/btc-keygen/releases/latest/download/btc-keygen-macos-aarch64
shasum -a 256 btc-keygen-macos-aarch64
chmod +x btc-keygen-macos-aarch64
./btc-keygen-macos-aarch64 generate

macOS may show a Gatekeeper warning. To bypass: xattr -d com.apple.quarantine btc-keygen-macos-aarch64.

macOS (Intel)

curl -LO https://github.com/aguimaraes/btc-keygen/releases/latest/download/btc-keygen-macos-x86_64
shasum -a 256 btc-keygen-macos-x86_64
chmod +x btc-keygen-macos-x86_64
./btc-keygen-macos-x86_64 generate

Windows

Get-FileHash btc-keygen-windows-x86_64.exe -Algorithm SHA256
.\btc-keygen-windows-x86_64.exe generate

Download btc-keygen-windows-x86_64.exe from the release page.

Build from source

Requires Rust and a C compiler (gcc, clang, or MSVC).

git clone https://github.com/aguimaraes/btc-keygen.git
cd btc-keygen
cargo build --release
./target/release/btc-keygen generate

BSD

Pre-built binaries are not provided for BSD. Build from source using the instructions above. The tool compiles and runs on FreeBSD, OpenBSD, and NetBSD.

Usage

Generate a keypair

$ btc-keygen generate
address: bc1q...
wif: K...

Prints the Bitcoin address and the WIF private key. That is all you need to receive and later spend Bitcoin.

Include the raw private key in hex

$ btc-keygen generate --hex
address: bc1q...
wif: K...
private_key_hex: ab12cd...

Include the compressed public key

$ btc-keygen generate --pubkey
address: bc1q...
wif: K...
pubkey_hex: 02ab12cd...

JSON output (for scripts)

$ btc-keygen generate --json
{"address":"bc1q...","wif":"K..."}

All options combined

$ btc-keygen generate --hex --pubkey --json

Provide your own private key

If you have your own 32 bytes of key material (for example, from physical dice rolls converted to hex), you can skip OS entropy and use your bytes directly. The tool validates that the value is a valid secp256k1 scalar, then derives the WIF, public key, and address from it.

$ btc-keygen generate --from-hex 0000000000000000000000000000000000000000000000000000000000000001
address: bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
wif: KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn

With --from-hex, the security of the generated key depends entirely on how you sourced those 32 bytes. The OS random number generator is not used.

What each field means

address
Your Bitcoin receiving address. Share it with anyone who wants to send you Bitcoin. Starts with bc1q (native SegWit).
wif
Wallet Import Format — your private key encoded in a standard format that any Bitcoin wallet can read. Starts with K or L. Keep it secret. See Using your keys for how to use it.
private_key_hex
The raw 32-byte private key in hexadecimal. Same key as the WIF, different format. Optional.
pubkey_hex
The compressed public key derived from the private key. Not secret, but not needed for basic use. Optional.

Frequently asked questions

Can I run this tool again to get the same key?

No. Every run generates a completely new, random keypair. There is no way to recreate a previous key. Write it down the first time.

What if I lose my private key?

Any Bitcoin sent to that address is permanently inaccessible. There is no recovery mechanism. This is by design — if anyone could recover your key, so could an attacker.

Why not use a hardware wallet?

Hardware wallets are a good option. This tool is an alternative for people who prefer not to trust a hardware device manufactured by a third party, or who want a simple, verifiable way to generate a single key for long-term storage.

Why not use a BIP39 mnemonic (seed words)?

Mnemonic phrases are useful for HD wallets that generate many addresses. This tool intentionally generates a single standalone key. It's simpler, easier to audit, and sufficient for cold storage of a single address. BIP39 support may be considered in a future version.

Is this a wallet?

No. This is a key generator. It creates a keypair and exits. It cannot send Bitcoin, check balances, or interact with the Bitcoin network in any way. To spend Bitcoin, you'll need to import the private key into a wallet application. See Using your address and private key for step-by-step instructions.

For developers

btc-keygen is also available as a Rust library. If you are building software that needs to generate Bitcoin keys, you can use it as a dependency instead of shelling out to the binary.

Add to your project

cargo add btc-keygen

API

The library exposes four functions and one type:

functioninputoutput
generate() Result<PrivateKey, Error>
encode_wif(&key) &PrivateKey String starts with K/L
derive_pubkey(&key) &PrivateKey [u8; 33] compressed pubkey
derive_address(&pubkey) &[u8; 33] String Bech32 (bc1q…)

Example

use btc_keygen;

fn main() -> Result<(), btc_keygen::Error> {
    // 1. Generate a private key from OS randomness
    let key = btc_keygen::generate()?;

    // 2. Encode as WIF (for wallet import)
    let wif = btc_keygen::encode_wif(&key);

    // 3. Derive the compressed public key
    let pubkey = btc_keygen::derive_pubkey(&key);

    // 4. Derive the Bitcoin address
    let address = btc_keygen::derive_address(&pubkey);

    println!("Address: {address}");
    println!("WIF:     {wif}");
    Ok(())
}

PrivateKey automatically zeroizes its bytes when dropped — you do not need to clear it manually.

Full API documentation is available on docs.rs.