BIP39 助记词生成原理与 Python 实现

BIP39 定义了一套将随机数编码为可记忆单词的标准,大多数钱包(MetaMask、Phantom、imToken)都支持。

BIP39 原理

  1. 生成 128/160/192/224/256 位随机熵
  2. 取熵的 SHA-256 哈希前 len/32 位作为校验位,拼接到熵末尾
  3. 每 11 位映射一个 BIP39 词表单词(共 2048 个词)
  4. 最终得到 12/15/18/21/24 个单词
熵长度校验位总位数单词数
128 bit4 bit132 bit12
256 bit8 bit264 bit24

Python 实现(bip-utils)

pip install bip-utils

从助记词派生以太坊私钥和地址

from bip_utils import (
    Bip39SeedGenerator,
    Bip39MnemonicValidator,
    Bip44,
    Bip44Coins,
    Bip44Changes,
)

mnemonic = "word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12"

# 可选:验证助记词是否合法
if not Bip39MnemonicValidator().IsValid(mnemonic):
    raise ValueError("助记词无效")

# 助记词 → 种子(可选 passphrase)
seed = Bip39SeedGenerator(mnemonic).Generate(passphrase="")

# 种子 → BIP44 派生路径 m/44'/60'/0'/0/0
ctx = Bip44.FromSeed(seed, Bip44Coins.ETHEREUM)
addr_ctx = (
    ctx.Purpose()
       .Coin()
       .Account(0)
       .Change(Bip44Changes.CHAIN_EXT)
       .AddressIndex(0)
)

private_key_hex = addr_ctx.PrivateKey().Raw().ToHex()
address = addr_ctx.PublicKey().ToAddress()

print(f"私钥: {private_key_hex}")
print(f"地址: {address}")

派生多个地址

for i in range(5):
    addr_ctx = (
        ctx.Purpose()
           .Coin()
           .Account(0)
           .Change(Bip44Changes.CHAIN_EXT)
           .AddressIndex(i)
    )
    print(f"[{i}] {addr_ctx.PublicKey().ToAddress()}")

Solana(BIP44 Coin 501)

from bip_utils import Bip44Coins

ctx = Bip44.FromSeed(seed, Bip44Coins.SOLANA)
sol_ctx = ctx.Purpose().Coin().Account(0).Change(Bip44Changes.CHAIN_EXT).AddressIndex(0)
print(f"Solana 地址: {sol_ctx.PublicKey().ToAddress()}")

手动验证 BIP39 校验和(纯 Python)

import hashlib

def validate_bip39(mnemonic: str, wordlist_path: str) -> bool:
    with open(wordlist_path) as f:
        words = f.read().splitlines()

    word_index = {w: i for i, w in enumerate(words)}
    mnemonic_words = mnemonic.strip().split()

    bits = ""
    for word in mnemonic_words:
        if word not in word_index:
            return False
        bits += format(word_index[word], "011b")

    # 最后 len/33 位是校验位
    cs_len = len(bits) // 33
    entropy_bits = bits[:-cs_len]
    checksum_bits = bits[-cs_len:]

    # 还原熵字节
    entropy = int(entropy_bits, 2).to_bytes(len(entropy_bits) // 8, "big")

    # SHA256 前 cs_len 位
    h = hashlib.sha256(entropy).digest()
    expected_cs = format(h[0], "08b")[:cs_len]

    return checksum_bits == expected_cs

注意事项

  • 助记词等同于钱包全部资产的控制权,不能在任何联网代码中明文存储
  • 生产环境派生私钥只能在离线机器或 HSM 上操作
  • 仅用于工具开发/验证时,建议使用测试网地址,不要导入真实资产