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
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
KorL) — "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:
- Install a Bitcoin wallet that supports WIF import — for example Electrum, BlueWallet, or Sparrow Wallet.
- Look for an option called Import, Sweep, or Import Private Key. Exact wording varies by wallet.
- Enter or scan the WIF string — the one starting with
KorL. - The wallet will recognize the key, find the associated address, and show your balance.
- 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.
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
KorL. 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:
| function | input | output |
|---|---|---|
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.