Skip to content

Keys

Proper key management is the foundation of blockchain security. The private keys that control access to blockchain accounts are the most critical assets in any blockchain infrastructure. A single key compromise can result in complete loss of funds, while key loss means permanent inaccessibility. This chapter covers comprehensive strategies for secure key generation, storage, and management for both development and production blockchain operations.


Private keys are the fundamental cryptographic secrets that control blockchain accounts. They are typically 256-bit numbers represented as 64 hexadecimal characters.

Private Key Structure:
┌─────────────────────────────────────────┐
│ Private Key (256-bit / 32 bytes) │
│ Format: 64 hex characters (0-9, a-f) │
│ Example: │
│ 8f2a559...3a7e0c5d8a2b4f9e1c3a6d9... │
└─────────────────────────────────────────┘
PropertyValue
Length256 bits (32 bytes)
Format64 hex characters
Entropy~10^77 combinations
Security LevelQuantum-resistant

Public keys are derived from private keys using elliptic curve cryptography and can be shared freely.

Private Key → Public Key (ECDSA secp256k1)
8f2a559... → 02a50eb66887d1fe3...
04d9f2a2a4f4c5...
(uncompressed)
Key TypeShareableDerivable
Private KeyNeverNo
Public KeyYesFrom private only
AddressYesFrom public key
FormatDescriptionExample
HexRaw hexadecimal8f2a559...3a7e
KeystoreEncrypted JSONUTC--2024-01-01--xxxx.json
Mnemonic12-24 word phraseabandon apple...
Base64Encoded binaryjypoYXNkZjo=
{
"address": "0x1234567890abcdef1234567890abcdef12345678",
"crypto": {
"cipher": "aes-128-ctr",
"ciphertext": "a8b3c4d5...",
"cipherparams": {
"iv": "aabbccdd11223344556677889900aabbcc"
},
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"n": 262144,
"r": 8,
"p": 1,
"salt": "saltvalue1234567890abcdef"
},
"mac": "macvalue1234567890abcdef..."
},
"id": "uuid-string",
"version": 3
}

Mnemonic codes (BIP-39) convert entropy into human-readable words:

Word CountEntropySecurity
12 words128 bitsGood
24 words256 bitsExcellent
Mnemonic to Key Derivation:
Word List (BIP-39) → BIP-32 HD Key → Ethereum Key
↓ ↓ ↓
[abandon, apple, ..., zoo] → Master Key → m/44'/60'/0'/0/0
Account Address

Terminal window
# Create new account (will prompt for password)
geth account new
# Create with password file
geth account new --password /path/to/password.txt
# Create multiple accounts
geth account new --password /path/to/pass1.txt
geth account new --password /path/to/pass2.txt
# List existing accounts
geth account list
# Update account password
geth account update 0x...
# Import private key
geth account import /path/to/private-key-file
Terminal window
# List connected hardware wallets
geth account list --keystore /path/to/hardware
# Use with hardware wallet (requires unlock)
geth --keystore /path/to/hardware \
--ledger \
--new-account
Terminal window
# Install eth-keys
pip install eth-keys eth-keyfile
# Create key programmatically
python3 << 'EOF'
from eth_keys import keys
# Generate new private key
private_key = keys.PrivateKey()
print(f"Private Key: {private_key.hex()}")
print(f"Public Key: {private_key.public_key.hex()}")
print(f"Address: {private_key.public_key.to_checksum_address()}")
EOF
Terminal window
pip install web3
python3 << 'EOF'
from web3 import Web3
# Generate random account
account = Web3().eth.account.create()
print(f"Address: {account.address}")
print(f"Private Key: {account.key.hex()}")
EOF
Terminal window
# Generate random wallet
cast wallet new
# Create from mnemonic
cast wallet import "mnemonic..."
# Sign transaction
cast send <to> <value> --private-key <key> --rpc-url <url>

┌─────────────────────────────────────────────────────────────┐
│ KEY SECURITY HIERARCHY │
├─────────────────────────────────────────────────────────────┤
│ │
│ Level 1: Development/Test Keys │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • File-based keystore with encryption │ │
│ │ • Stored in encrypted filesystem │ │
│ │ • Regular rotation (monthly) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Level 2: Staging/Testing Keys │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • Hardware security module (HSM) │ │
│ │ • Multi-signature required │ │
│ │ • Encrypted backup storage │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Level 3: Production Keys (Validators) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • Hardware wallets (Ledger, Trezor) │ │
│ │ • HSM with key sharding │ │
│ │ • Multi-party computation (MPC) │ │
│ │ • Geographic distribution │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
  1. Never share private keys

    • Don’t commit to version control
    • Don’t send over unencrypted channels
    • Don’t log or display in plain text
  2. Use hardware wallets for significant value

    • Ledger hardware wallets
    • Trezor devices
    • AWS CloudHSM
    • Azure Key Vault HSM
  3. Maintain secure backups

    • Multiple encrypted copies
    • Geographic separation
    • Test restoration regularly
  4. Implement key separation

    • Different keys for different purposes
    • Operational vs. signing keys
    • Staging vs. production keys
  5. Enable multi-signature

    • Require multiple approvals for large transactions
    • Use Gnosis Safe for Ethereum
    • Implement timelock delays
RoleCan SignCan ViewCan ExportCan Backup
OperatorYesAddress OnlyNoNo
AuditorNoYesNoNo
AdminNoYesNoYes
Security OfficerYesYesEncrypted OnlyYes

/data/ethereum/
├── keystore/ # Encrypted keystore files
│ ├── UTC--2024-01-01T00-00-00.000Z--0x1234...abcd.json
│ ├── UTC--2024-01-15T00-00-00.000Z--0x5678...efgh.json
│ └── ...
├── nodekey # Node's private key (P2P)
├── obtuse/ # Light client keys
└── trustdb/ # Trusted node keys
Terminal window
# Create encrypted directory (LUKS)
sudo cryptsetup luksFormat /dev/sdb1
sudo cryptsetup luksOpen /dev/sdb1 eth_keys
sudo mkfs.ext4 /dev/mapper/eth_keys
sudo mount /dev/mapper/eth_keys /data/ethereum/keystore
# Or use encfs (FUSE-based)
sudo apt-get install encfs
encfs /encrypted/keys /keystore
Terminal window
# Set private key via environment variable
export PRIVATE_KEY="0xabcd..."
# In your application
const privateKey = process.env.PRIVATE_KEY;

⚠️ WARNING: Environment variables can leak through:

  • Process listing (ps aux)
  • Log files
  • Docker container inspection
  • Error reporting systems
Terminal window
# Using HashiCorp Vault
vault write secret/ethereum/key @key.json
# Retrieve at runtime
vault read -field=key secret/ethereum/key
# Using AWS Secrets Manager
aws secretsmanager get-secret-value \
--secret-id ethereum/production-key \
--query SecretString \
--output text

Terminal window
# Install ledger library
pip install ledgereth
# Get address from Ledger
ledgereth --address
# Sign transaction
ledgereth --transaction tx.json
Terminal window
# Install trezor library
pip install trezor
# Get address from Trezor
trezorctl get-address --address-n 0/0
# Sign transaction
trezorctl sign-tx --file tx.json
Terminal window
# Using PKCS#11 with YubiHSM
yubihsm> put asecret 0 label="eth_key" domains=1,2 keytype=ecdsa params=1 algorithm=ecc_secp256k1
# Sign transaction with HSM
pkcs11-tool --module /usr/lib/yubihsm-pkcs11.so \
--login --pin=123456 \
--sign -m ECDSA \
--input-file tx-digest.bin \
--output-file signature.bin
Terminal window
# Generate key in CloudHSM
key_mgmt_util Gensk -k 1 -m 12345678ABCD...
# Sign using CloudHSM
gengetattr_QSN -k 1 -m 12345678ABCD... -d 32 -s input.txt -S signature.bin

Key Rotation Policy:
┌─────────────┬──────────────┬──────────────┐
│ Key Type │ Rotation │ Notes │
├─────────────┼──────────────┼──────────────┤
│ Development │ Weekly │ Easy to │
│ │ │ regenerate │
├─────────────┼──────────────┼──────────────┤
│ Staging │ Monthly │ Coordinate │
│ │ │ with releases│
├─────────────┼──────────────┼──────────────┤
│ Production │ Quarterly │ Requires │
│ (Hot) │ │ planning │
├─────────────┼──────────────┼──────────────┤
│ Production │ Annually │ Full audit │
│ (Cold) │ │ required │
└─────────────┴──────────────┴──────────────┘
Terminal window
# Step 1: Create new key
geth account new --password /path/to/new_password.txt
# Step 2: Transfer funds (if applicable)
geth attach http://localhost:8545
> eth.sendTransaction({from: "0xOLD...", to: "0xNEW...", value: web3.toWei(10, "ether")})
# Step 3: Update configurations to use new key
# Edit systemd service files
# Update environment variables
# Refresh any cached references
# Step 4: Securely delete old key
shred -u /path/to/old/keystore/file.json
# Step 5: Verify everything works
geth account list
rotate_key.sh
#!/bin/bash
OLD_KEY_PATH="/data/ethereum/keystore/UTC--2024-01-01--old.json"
NEW_PASSWORD="/path/to/new_password.txt"
NEW_KEY_PATH="/data/ethereum/keystore/"
# Generate new key
echo "Creating new key..."
NEW_KEY=$(geth account new --password $NEW_PASSWORD | grep "Address:" | cut -d'{' -f2 | cut -d'}' -f1)
echo "New address: $NEW_KEY"
# Archive old key
if [ -f "$OLD_KEY_PATH" ]; then
mv "$OLD_KEY_PATH" "/data/ethereum/keystore/archive/"
echo "Old key archived"
fi
# Update alias (symlink)
ln -sf "$NEW_KEY_PATH" "/data/ethereum/keystore/current"
# Restart services
systemctl restart geth
echo "Key rotation complete"

Terminal window
# Using mnemonics
pip install eth-account
python3 << 'EOF'
from eth_account import Account
# Recover from 12-word mnemonic
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
account = Account.from_mnemonic(mnemonic)
print(f"Address: {account.address}")
# Derive specific path
account = Account.from_mnemonic(mnemonic, account_path="m/44'/60'/0'/0/1")
print(f"Second account: {account.address}")
EOF
Terminal window
# Decrypt keystore file
pip install eth-keyfile
python3 << 'EOF'
import getpass
import json
from eth_keyfile import decode_keyfile
# Decrypt keystore
with open('keystore.json', 'rb') as f:
password = getpass.getpass('Enter password: ')
private_key = decode_keyfile(f.read(), password.encode())
print(f"Private key: {private_key.hex()}")
EOF
verify_backup.sh
#!/bin/bash
BACKUP_DIR="/backup/ethereum/keystore"
echo "Verifying key backup integrity..."
# Check all keystore files
for file in $BACKUP_DIR/*.json; do
echo "Checking $file..."
# Validate JSON format
if ! jq empty "$file" 2>/dev/null; then
echo "ERROR: Invalid JSON in $file"
exit 1
fi
# Check address exists
address=$(jq -r '.address' "$file")
echo " Address: $address"
done
echo "All backup keys verified successfully"

Validator Key Structure:
┌─────────────────────────────────────────────────────────────┐
│ VALIDATOR KEYS │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Signing Key (nodekey) │ │
│ │ • Used for P2P networking │ │
│ │ • Can be rotated │ │
│ │ • Low security impact if compromised │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Validator Private Key │ │
│ │ • Signs blocks/proposals │ │
│ │ • Must be highly secured │ │
│ │ • Often stored in HSM │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Withdrawal Key │ │
│ │ • Controls stake/withdrawals │ │
│ │ • Cold storage recommended │ │
│ │ • Rarely used (only for major operations) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Terminal window
# Generate validator keys (Eth2)
eth2valtools mnemonic-entropy > mnemonic.txt
# Or use standard 24-word
eth2valtools mnemonic
# Create validator keys
eth2valtools eth1-credebial \
--mnemonic="your 24 word mnemonic" \
--validation-keys-dir=/validator/keys \
--deposit-data-dir=/validator/deposit \
--validators-mnemonic-path=/validator/mnemonic.txt
# Secure mnemonic
# Store in secure location (safe, HSM)
Terminal window
# Generate Tendermint validator key
gaiad init validator --home /etc/gaia
# Get validator key
cat /etc/gaia/config/priv_validator_key.json
# Get public key
gaiad tendermint show-validator --home /etc/gaia
# Backup (CRITICAL)
cp /etc/gaia/config/priv_validator_key.json /backup/secure/
cp /etc/gaia/config/priv_validator_key.json /backup/secondary/

audit_keys.sh
#!/bin/bash
echo "=== Ethereum Key Audit ==="
echo "Date: $(date)"
echo ""
# List all keystore files
echo "Active Keys:"
for f in /data/ethereum/keystore/UTC--*.json; do
addr=$(jq -r '.address' "$f")
created=$(jq -r '.version' "$f")
echo " $addr - Created: $created"
done
echo ""
echo "Archive Keys:"
for f in /data/ethereum/keystore/archive/UTC--*.json; do
addr=$(jq -r '.address' "$f")
echo " $addr (archived)"
done
echo ""
echo "Hardware Wallets:"
ledgereth --list
echo ""
echo "Key Usage Summary:"
echo " Production: $(ls /data/ethereum/keystore/UTC--*prod*.json | wc -l)"
echo " Staging: $(ls /data/ethereum/keystore/UTC--*staging*.json | wc -l)"
echo " Development: $(ls /data/ethereum/keystore/UTC--*dev*.json | wc -l)"
Terminal window
# Enable audit logging for key access
export KEY_AUDIT_LOG="/var/log/ethereum/key-access.log"
# Log key usage
log_key_access() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $USER - $1 - $2" >> $KEY_AUDIT_LOG
}
# Usage
log_key_access "sign" "0x1234...abcd"
log_key_access "export" "0x1234...abcd"

emergency_key_revocation.sh
#!/bin/bash
COMPROMISED_ADDRESS="$1"
NETWORK="$2" # mainnet, testnet, etc.
if [ -z "$COMPROMISED_ADDRESS" ]; then
echo "Usage: $0 <address> <network>"
exit 1
fi
echo "EMERGENCY: Key compromised - $COMPROMISED_ADDRESS"
# 1. Transfer remaining funds
echo "Step 1: Transferring remaining funds..."
geth attach http://localhost:8545
# Manual intervention required:
# > eth.sendTransaction({from: "$COMPROMISED_ADDRESS", to: "0xNEW...", value: ...})
# 2. Revoke authorizations (if validator)
echo "Step 2: Revoking validator authorizations..."
# 3. Notify team
echo "Step 3: Sending incident notification..."
# send_alert "Key compromised: $COMPROMISED_ADDRESS"
# 4. Archive compromised key
echo "Step 4: Archiving compromised key..."
mkdir -p /compromised/$(date +%Y%m%d)
mv /data/ethereum/keystore/*$COMPROMISED_ADDRESS* /compromised/$(date +%Y%m%d)/
# 5. Document incident
echo "Step 5: Documenting incident..."
echo "$(date): Key $COMPROMISED_ADDRESS compromised" >> /var/log/security/incidents.log
echo "Emergency procedures initiated"
Terminal window
# Recovery from mnemonic
python3 << 'EOF'
from eth_account import Account
mnemonic = input("Enter your 24-word mnemonic: ")
account = Account.from_mnemonic(mnemonic)
print(f"Recovered address: {account.address}")
print(f"Private key: {account.key.hex()}")
EOF

  • Private keys are the foundation of blockchain security - protect them at all costs
  • Use hardware wallets for production/validator keys
  • Implement key rotation on regular schedules
  • Maintain encrypted backups in geographically separated locations
  • Use secrets management systems for production environments
  • Implement multi-signature for high-value transactions
  • Have emergency procedures ready for key compromise scenarios
  • Keep detailed audit logs of all key operations
  • Separate keys by environment and use case

In Chapter 33: Network Security, we’ll explore network-level security measures.


Last Updated: 2026-02-22