Bitcoin Key Generation Rules and Go Implementation

·

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:

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

FormatPrefixUse Case
RawNoneInternal use, 32 bytes
HexNoneDebugging, 64-character hex string
WIF (Wallet Import Format)5Standard format for importing/exporting
WIF-compressedK or LFor 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

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:

  1. Apply SHA-256 to the public key.
  2. Apply RIPEMD-160 to the SHA-256 result → yields a 160-bit (20-byte) hash.
  3. Prepend network version byte:

    • 0x00 for mainnet → starts with 1
    • 0x6F for testnet → starts with m or n
  4. Double-SHA256 the result to generate a 4-byte checksum.
  5. 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:

TypeVersion (Hex)Encoded Prefix
Mainnet Address0x001
P2SH Address0x053
WIF Private Key0x805, K, or L
BIP32 Extended Public Key0x0488B21Expub

The process:

  1. Version byte + data
  2. Double-SHA256 → take first 4 bytes as checksum
  3. Concatenate: version + data + checksum
  4. 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:

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:       14XxNrUcQQ7hM1VMeNqp3vCVvhv3F7ZgPZ

You 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

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.