区块链-助记词、种子、秘钥、Keystore

关系图

sequenceDiagram 随机数-->>随机数:生成128~256位随机数 随机数->>助记词: BIP-39规范 助记词->>种子: 助记词+口令(PBKDF2算法) 种子->>私钥: 获得 私钥-->>私钥: 创建公钥 私钥-->>私钥: 创建地址

步骤说明

要创建一个HD钱包(私钥),必须先有一个确定的512bit(64字节)的随机数种子。

1、随机数

  • 随机数安全性决定了种子的安全性,所以这里要密码学的安全随机数;
  • 随机数的总位数必须是32的倍数,常用128或256位;
  • 256位的随机数,又叫256的熵;

2、随机数生成助记词

  • BIP-39规范提出了通过助记词生成种子的算法;
  • 总共2048个单词,也可以是其他语言,请参考助记词表
  • 根据第1步生成的安全随机数计算种子:
    • 用SHA-256计算随机数的校验码,并取若干位追加到随机数末尾,使得随机数+校验码的总位数是11的倍数;
    • 按每11位合成一个数字,得到范围是0~2047的24个整数,并作为索引从2048个助记词表里面取得24个助记词;

3、生成种子(Seed)

  • 有了助记词之后,使用PBKDF2算法和Hmac-SHA512函数产生种子;
  • 下面是一段开源的Dart代码供参考:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Uint8List mnemonicToSeed(List<String> mnemonic, {String passphrase = ''}) {
  return pbkdf2(mnemonic, passphrase: passphrase);
}

const _saltPrefix = 'mnemonic';

/// Password-Based Key Derivation Function 2.
Uint8List pbkdf2(
  List<String> mnemonic, {
  String passphrase = '',
  int blockLength = 128,
  int iterationCount = 2048,
  int desiredKeyLength = 64,
}) {
  final derivator = PBKDF2KeyDerivator(HMac(SHA512Digest(), blockLength));
  final salt = Uint8List.fromList(utf8.encode(_saltPrefix + passphrase));

  derivator.init(Pbkdf2Parameters(salt, iterationCount, desiredKeyLength));

  List<int> codeUnits = [];
  for (final word in mnemonic) {
    codeUnits.addAll(word.codeUnits);
    codeUnits.add(32); //Space.
  }

  codeUnits.removeLast();

  return derivator.process(Uint8List.fromList(codeUnits));
}
  • 输入助记词列表,转换为UTF-8编码的列表,并设置salt为mnemonic+用户口令,循环2048次,最终生成64字节种子。
  • 所以,如果助记词相同并且口令相同,那么就能产生相同的种子。随机数越随机,产生的助记词也就越随机,那么种子也就越随机。

4、公钥私钥

  • 种子数据其实就是私钥;
  • 用椭圆曲线算法就能产生公钥和地址;
  • 下面是Dart的以太坊代码:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Ethereum extends Coin {
  const Ethereum();

  @override
  PrivateKey createPrivateKey(Uint8List seed) {
    final bn = decodeBigIntWithSign(seed);

    return PrivateKey(bn);
  }

  @override
  PublicKey createPublicKey(PrivateKey privateKey) =>
      PublicKey(EC.secp256k1.createPublicKey(privateKey.value, true));

  @override
  String createAddress(PublicKey publicKey) {
    var compressed = EC.secp256k1.uncompressPublicKey(publicKey.value);
    final input = compressed.sublist(1);

    final address = KeccakDigest(256).process(input);
    final w = address.skip(address.length - 20).toList();

    final h = hex.encode(w);
    final f = toChecksumAddress(h);

    return '0x$f';
  }

  @override
  Uint8List generateSignature(PrivateKey privateKey, Uint8List message) {
    final signature = EC.secp256k1.generateSignature(privateKey.value, message);

    final sgn = toDER(signature);

    return sgn;
  }

  @override
  bool verifySignature(
    PublicKey publicKey,
    Uint8List message,
    Uint8List signature,
  ) {
    final sgn = fromDER(signature);

    final result = EC.secp256k1.verifySignature(publicKey.value, message, sgn);

    return result;
  }
}

引用或参考

廖雪峰-助记词
密钥、地址、钱包
dart_wallet 开源

Built with Hugo
主题 StackJimmy 设计