Understanding how Bitcoin keys and addresses are generated is essential for developers diving into blockchain technology. This article explains the cryptographic principles behind Bitcoin key generation, explores the structure of private and public keys, and demonstrates a practical implementation in Go. Whether you're building a wallet, analyzing transactions, or simply curious about cryptography, this guide provides a clear and technical walkthrough.
Types of Bitcoin Keys
Bitcoin uses three main types of keys, all encoded using Base58Check for human readability and error detection:
- Private Key: A 256-bit random number used to sign transactions and prove ownership.
- Public Key: Derived from the private key via elliptic curve multiplication.
- Public Key Hash (Bitcoin Address): A shortened version of the public key, commonly used as a recipient address.
These keys form a one-way hierarchy:
Private Key → Public Key → Public Key Hash (Address)Each step is irreversible due to cryptographic hashing and elliptic curve mathematics. Your private key must remain secret—losing it means losing access to your funds; exposing it risks theft.
👉 Learn how secure crypto wallets manage private keys with advanced protocols.
Key Formats Explained
While raw keys are binary data, various encoding formats make them usable across wallets and systems.
Private Key Formats
| Format | Prefix | Use Case |
|---|---|---|
| Raw | None | Internal use, 32 bytes |
| Hex | None | Debugging, 64-character hex string |
| WIF (Wallet Import Format) | 5 | Standard format for importing/exporting |
| WIF-compressed | K or L | For compressed public keys only |
WIF encoding uses Base58Check, which includes a version byte (0x80 for mainnet) and a 4-byte checksum for error detection.
Note: A WIF-compressed key ends with an appended 0x01 before encoding, signaling that the derived public key should be compressed.Public Key Formats
- Uncompressed: Starts with
04, followed by 32-byte X and 32-byte Y coordinates (65 bytes total). - Compressed: Uses
02if Y is even,03if Y is odd, followed by only the X coordinate (33 bytes).
Compression halves storage size—critical when handling millions of transactions. Since Y can be derived from X using the elliptic curve equation $ y^2 = x^3 + 7 $, storing just X suffices.
From Public Key to Bitcoin Address: The Hashing Process
To convert a public key into a Bitcoin address:
- Apply SHA-256 to the public key.
- Apply RIPEMD-160 to the SHA-256 result → yields a 160-bit (20-byte) hash.
Prepend network version byte:
0x00for mainnet → starts with10x6Ffor testnet → starts withmorn
- Double-SHA256 the result to generate a 4-byte checksum.
- Append checksum and encode with Base58Check.
This ensures compactness, uniqueness, and built-in validation.
What Is Base58Check Encoding?
Base58Check encodes binary data into readable strings while minimizing transcription errors. It excludes easily confused characters like 0, O, l, and I, making it ideal for manual entry.
Common prefixes in Base58Check:
| Type | Version (Hex) | Encoded Prefix |
|---|---|---|
| Mainnet Address | 0x00 | 1 |
| P2SH Address | 0x05 | 3 |
| WIF Private Key | 0x80 | 5, K, or L |
| BIP32 Extended Public Key | 0x0488B21E | xpub |
The process:
- Version byte + data
- Double-SHA256 → take first 4 bytes as checksum
- Concatenate: version + data + checksum
- Encode with Base58
Decoding reverses these steps, including checksum verification.
👉 Discover how modern wallets use deterministic key derivation securely.
Step-by-Step: Generating Keys in Go
We'll implement Bitcoin key generation using Go’s crypto/ecdsa, crypto/elliptic, and hashing libraries.
1. Generate an ECDSA Key Pair
Bitcoin uses the secp256k1 elliptic curve.
func newKeyPair() ([]byte, []byte) {
curve := elliptic.P256()
private, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
log.Panic(err)
}
d := private.D.Bytes()
privKey := paddedAppend(32, []byte{}, d)
pubKey := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...)
return privKey, pubKey
}Here:
Dis the private key scalar.XandYare coordinates of the public key point on the curve.
2. Compress the Public Key (Optional but Recommended)
func compressPubKey(x, y *big.Int) []byte {
prefix := []byte{0x02}
if y.Bit(0) == 1 {
prefix[0] = 0x03
}
return append(prefix, paddedAppend(32, []byte{}, x.Bytes())...)
}This reduces the public key from 65 to 33 bytes.
3. Hash Public Key to Create Address
func hashPubKey(pub []byte) []byte {
sha256h := sha256.Sum256(pub)
ripemd160h := ripemd160.New()
ripemd160h.Write(sha256h[:])
return ripemd160h.Sum(nil)
}4. Base58Check Encode
func b58checkencode(version byte, payload []byte) string {
data := append([]byte{version}, payload...)
// Double SHA256 for checksum
first := sha256.Sum256(data)
second := sha256.Sum256(first[:])
checksum := second[0:4]
data = append(data, checksum...)
encoded := b58encode(data)
// Handle leading zero bytes
for _, b := range data {
if b != 0 { break }
encoded = "1" + encoded
}
return encoded
}The final output is your Bitcoin address—starting with 1 for standard addresses.
Practical Example Output
Running the full implementation yields:
Private Key (Hex): 8A7FD53F196F0CCFDC977A1CD0A035ACE70741B6BDB3DE58D5C3C1BEA68A3798
Public Key (Raw): 4DB3EADC34F02F92A512730B4B69E7FDC07659A34363AA5B703898E0CA35015F9D1DBE721C39EA8BAABBD9044D249DCE713AF0668237CB8685A7ABE8CEB857CA
SHA-256 Hash: 4D67DE558B00A9FD3E942CA1A1CCD60FA9E5DBE4CBF69D89ABF1073330C6E240
RIPEMD-160 Hash: 26C31518D393638CCF57DF964C946499E001D6C4
Version + Payload: 0026C31518D393638CCF57DF964C946499E001D6C4
Checksum (first 4): 010742B8
Final Binary: 0026C31518D393638CCF57DF964C946499E001D6C4010742B8
Bitcoin Address: 14XxNrUcQQ7hM1VMeNqp3vCVvhv3F7ZgPZYou can verify this address on any blockchain explorer.
Frequently Asked Questions
Q: Can I recover my Bitcoin if I lose my private key?
A: No. Without the private key, there's no way to sign transactions or prove ownership. Always back up your keys securely.
Q: What's the difference between WIF and WIF-compressed?
A: WIF-compressed includes a suffix (0x01) before encoding, indicating that the corresponding public key should be compressed. Both are valid but produce different addresses.
Q: Why use RIPEMD-160 after SHA-256?
A: SHA-256 provides strong hashing; RIPEMD-160 further compresses the result to 160 bits, aligning with Bitcoin’s address length while adding an extra layer of security.
Q: Are all Bitcoin addresses derived from public keys?
A: Most are, but some (like P2SH addresses starting with "3") come from scripts. These allow complex spending conditions like multi-signature setups.
Q: Is Base58Check reversible?
A: Yes—Base58Check is a two-way encoding with checksum validation. You can decode an address back to its binary form safely.
Q: How do wallets ensure randomness in private key generation?
A: They use cryptographically secure random number generators (CSPRNGs). Predictable keys risk theft through brute-force attacks.
👉 Explore secure key management practices used by leading crypto platforms.
Core Keywords
- Bitcoin key generation
- Private key format
- Public key compression
- Base58Check encoding
- Elliptic curve cryptography
- secp256k1
- RIPEMD-160 hash
- Go blockchain development
By understanding these fundamentals, developers can build more secure applications and gain deeper insight into how Bitcoin ensures ownership and integrity at the protocol level.