Public Key Cryptography

Understanding public-private key pairs and their use in blockchain addresses.

Intermediate⏱️ 40 min📚 Prerequisites: 1

Public Key Cryptography

Public key cryptography (asymmetric cryptography) uses two keys: a public key (shared) and a private key (secret).

Key Concepts

  • Private Key: Secret, never shared, used to sign transactions
  • Public Key: Shared publicly, used to verify signatures
  • Address: Derived from public key, used to receive funds

Key Pair Generation

RUST
struct KeyPair {
    private_key: String,
    public_key: String,
    address: String,  // Derived from public key
}

fn generate_keypair() -> KeyPair {
    // In production: use cryptographic library
    // let (private, public) = generate_ecdsa_keypair();
    
    KeyPair {
        private_key: String::from("private_key_..."),
        public_key: String::from("public_key_..."),
        address: String::from("address_..."),
    }
}

Address Generation

Blockchain addresses are typically derived from public keys:

  1. Hash the public key (SHA-256 or similar)
  2. Take a portion of the hash
  3. Encode it (Base58, hex, etc.)
RUST
fn derive_address(public_key: &str) -> String {
    // Hash public key
    // let hash = sha256(public_key);
    // Take first 20 bytes for Ethereum-style address
    // Encode as hex
    format!("0x{}", &public_key[..20])
}

Wallet Structure

RUST
struct Wallet {
    keypair: KeyPair,
    balance: u64,
}

impl Wallet {
    fn new() -> Self {
        Wallet {
            keypair: generate_keypair(),
            balance: 0,
        }
    }
    
    fn address(&self) -> &str {
        &self.keypair.address
    }
}

Security Principles

  1. Never share private key: Anyone with it can spend your funds
  2. Public key is safe: Can be shared freely
  3. Address is public: Used to receive funds
  4. Backup private key: Loss = permanent fund loss

Use in Blockchain

  • Bitcoin: Public key → SHA-256 + RIPEMD-160 → Base58 address
  • Ethereum: Public key → Keccak-256 → Last 20 bytes → Hex address
  • Key derivation: HD wallets use BIP32/BIP44

Code Examples

Key Pair Structure

Creating and using key pairs

RUST
struct KeyPair {
    private_key: String,
    public_key: String,
    address: String,
}
fn generate_keypair() -> KeyPair {
    let private_key = "private_key_abc123";
    let public_key = "public_key_xyz789";
    KeyPair {
        private_key: String::from(private_key),
        public_key: String::from(public_key),
        address: derive_address(public_key),
    }
}
fn derive_address(public_key: &str) -> String {
    format!("0x{}", &public_key[..20.min(public_key.len())])
}
fn main() {
    let keypair = generate_keypair();
    println!("Public Key: {}", keypair.public_key);
    println!("Address: {}", keypair.address);
}

Explanation:

Key pairs are the foundation of blockchain security. The private key must be kept secret, while the public key and address can be shared.

Wallet Implementation

Creating a wallet with key pair

RUST
struct KeyPair {
    private_key: String,
    public_key: String,
    address: String,
}
struct Wallet {
    keypair: KeyPair,
    balance: u64,
}
impl Wallet {
    fn new() -> Self {
        let keypair = KeyPair {
            private_key: String::from("secret_key"),
            public_key: String::from("public_key_12345678901234567890"),
            address: String::from("0x12345678901234567890"),
        };
        Wallet {
            keypair,
            balance: 0,
        }
    }
    fn address(&self) -> &str {
        &self.keypair.address
    }
    fn get_balance(&self) -> u64 {
        self.balance
    }
}
fn main() {
    let wallet = Wallet::new();
    println!("Wallet address: {}", wallet.address());
    println!("Balance: {}", wallet.get_balance());
}

Explanation:

A wallet contains a key pair and tracks balance. The address is used to receive funds, and the private key is used to authorize spending.

Exercises

Create Wallet

Create a wallet with a key pair and display its address!

Medium

Starter Code:

RUST
struct KeyPair {
    private_key: String,
    public_key: String,
    address: String,
}
struct Wallet {
    keypair: KeyPair,
}
fn main() {
    let wallet = Wallet::new();
    println!("Address: {}", wallet.address());
}