4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

永久保存版!ウォレットを使わずにニーモニックからSymbolの秘密鍵を導出する方法

Last updated at Posted at 2025-06-22

ある時、急にウォレットやSDKが使えなくなったとしても
ニーモニックを覚えておけば秘密鍵を導出することができます。
たとえ使うことがなかったとしても、避難訓練をしておきましょう。

これは非常時のスクリプトです。
暗号資産の入っていないニーモニックで検証するかテストネットで検証してください。

(async () => {
  const mnemonic = prompt("24語のニーモニックを入力(半角スペース区切り):");
  const passphrase = ""; // 必要あれば設定

  // 正規化関数
  const encoder = new TextEncoder();
  const normalize = (s) => s.normalize("NFKD");

  // BIP39 → シード(512bit = 64byte)
  const salt = "mnemonic" + passphrase;
  const keyMaterial = await crypto.subtle.importKey(
    "raw",
    encoder.encode(normalize(mnemonic)),
    { name: "PBKDF2" },
    false,
    ["deriveBits"]
  );
  const seedBuffer = await crypto.subtle.deriveBits(
    {
      name: "PBKDF2",
      salt: encoder.encode(normalize(salt)),
      iterations: 2048,
      hash: "SHA-512"
    },
    keyMaterial,
    512
  );

  const seed = new Uint8Array(seedBuffer);

  // HMAC-SHA512 utility
  async function hmacSha512(keyBytes, dataBytes) {
    const key = await crypto.subtle.importKey(
      "raw",
      keyBytes,
      { name: "HMAC", hash: "SHA-512" },
      false,
      ["sign"]
    );
    const sig = await crypto.subtle.sign("HMAC", key, dataBytes);
    return new Uint8Array(sig);
  }

  // BIP32 path derivation
  async function deriveEd25519PrivateKey(seed, path) {
    // m (master key)
    let key = await hmacSha512(
      encoder.encode("ed25519 seed"),
      seed
    );
    let priv = key.slice(0, 32);
    let chainCode = key.slice(32);

    const segments = path
      .split('/')
      .slice(1)
      .map(seg => {
        const hardened = seg.endsWith("'");
        const index = parseInt(hardened ? seg.slice(0, -1) : seg, 10);
        return (hardened ? (index + 0x80000000) : index) >>> 0;
      });

    for (let i = 0; i < segments.length; i++) {
      const index = segments[i];

      const data = new Uint8Array(1 + 32 + 4); // 0x00 + priv + index
      data[0] = 0x00;
      data.set(priv, 1);
      data[33] = (index >>> 24) & 0xff;
      data[34] = (index >>> 16) & 0xff;
      data[35] = (index >>> 8) & 0xff;
      data[36] = index & 0xff;

      const I = await hmacSha512(chainCode, data);
      priv = I.slice(0, 32);
      chainCode = I.slice(32);
    }

    return priv;
  }

  const derived = await deriveEd25519PrivateKey(seed, "m/44'/4343'/0'/0'/0'");
  const hex = Array.from(derived).map(b => b.toString(16).padStart(2, '0')).join('');

  console.log("✅ Symbol用秘密鍵(hex):");
  console.log(hex);
})();

実行する前に、ソースコード内部に importrequire が含まれていないことを確認してください。これらが含まれていると、入力したニーモニックを外部に送信されてしまう可能性があります(それはここで提供されたソースコードではありません)。

また、事前に拡張機能の無効化をしたりクリップボードを記録するソフトウェアも使用しないようにしましょう。

テストネットで検証する場合は

 const derived = await deriveEd25519PrivateKey(seed, "m/44'/4343'/0'/0'/0'");

の場所を

 const derived = await deriveEd25519PrivateKey(seed, "m/44'/1'/0'/0'/0'");

にしてください。

コード量圧縮版

(async () => {
  const mnemonic = prompt("24語のニーモニックを入力:");
  const enc = new TextEncoder(), norm = s => s.normalize("NFKD");

  const key = await crypto.subtle.importKey("raw", enc.encode(norm(mnemonic)), { name: "PBKDF2" }, false, ["deriveBits"]);
  const seed = new Uint8Array(await crypto.subtle.deriveBits({ name: "PBKDF2", salt: enc.encode("mnemonic"), iterations: 2048, hash: "SHA-512" }, key, 512));

  const hmac = async (k, d) => {
    const ck = await crypto.subtle.importKey("raw", k, { name: "HMAC", hash: "SHA-512" }, false, ["sign"]);
    return new Uint8Array(await crypto.subtle.sign("HMAC", ck, d));
  };

  const path = [44, 4343, 0, 0, 0].map(i => i + 0x80000000);

  let I = await hmac(enc.encode("ed25519 seed"), seed);
  let priv = I.slice(0, 32), chain = I.slice(32);

  for (const i of path) {
    const d = new Uint8Array(37);
    d[0] = 0; d.set(priv, 1);
    d.set([(i >>> 24) & 255, (i >>> 16) & 255, (i >>> 8) & 255, i & 255], 33);
    I = await hmac(chain, d);
    priv = I.slice(0, 32); chain = I.slice(32);
  }

  console.log("✅ Symbol用秘密鍵:\n" + [...priv].map(b => b.toString(16).padStart(2, '0')).join(''));
})();

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?