ssh-seed-keygen — Ed25519 SSH Keys You Can Recover from 24 Words
Source: git.jakobhusu.com/jakobhusu/ssh-seed-keygen
Yesterday I built age-seed-keygen — same concept, different key type. After finishing that one I figured SSH keys had the same problem, so I did it again.
The problem is simple: ssh-keygen gives you a file. If the file is gone, the key is gone. I've had to rotate SSH keys more times than I'd like because of dead drives and badly timed laptop replacements, and every time it means updating authorized_keys on whatever servers I care about. It's annoying. With a mnemonic backup you just recover the key, and nothing else needs to change.
How recovery works
Ed25519 private keys, like age X25519 keys, are 32 bytes of entropy under the hood. BIP39 encodes 32 bytes as exactly 24 words. So the same approach works: generate random bytes, derive both the key and the mnemonic from them, and the mnemonic becomes a paper backup that can recreate the key file at any time.
entropy = os.urandom(ENTROPY_BITS // 8)
words = Mnemonic("english").to_mnemonic(entropy)
private_key = Ed25519PrivateKey.from_private_bytes(entropy)
No KDF, no extra derivation steps — the entropy bytes go straight into the key. Same input, same key, always. The output is standard OpenSSH PEM format so it works everywhere ~/.ssh/id_ed25519 works.
Usage
$ python keygen.py generate -C you@example.com
Generated Ed25519 SSH key pair
Private key : /home/user/.ssh/id_ed25519
Public key : /home/user/.ssh/id_ed25519.pub
Your 24-word recovery mnemonic — write these down and store offline:
1. crane 2. ribbon 3. napkin 4. frozen 5. walnut 6. draft
7. pepper 8. sorrow 9. canoe 10. lemon 11. blanket 12. honest
13. voyage 14. signal 15. copper 16. pebble 17. gentle 18. marble
19. oyster 20. summit 21. velvet 22. fern 23. harvest 24. chorus
These words reconstruct your exact private key. Keep them secret.
Recovery:
$ python keygen.py recover -C you@example.com \
crane ribbon napkin frozen walnut draft \
pepper sorrow canoe lemon blanket honest \
voyage signal copper pebble gentle marble \
oyster summit velvet fern harvest chorus
After recovery, check the public key fingerprint against what's in your authorized_keys. If it matches, you're back to exactly where you were.
Custom output path and comment work the same as regular ssh-keygen:
$ python keygen.py generate -o ~/.ssh/id_work -C work@company.com
There's also a -p flag if you want to encrypt the key file with a passphrase. The passphrase protects the file on disk — it doesn't touch the mnemonic. If you recover later you can set a different passphrase, or none at all.
The obvious warning
The 24 words aren't a backup code or a hint. They encode the private key directly. Anyone with those words can reconstruct your key in a few seconds. Keep them offline, physically secure, and separate from the key file itself.
A note if you use both tools
If you're using both ssh-seed-keygen and age-seed-keygen, they technically use the same entropy format — you could back both keys from one mnemonic. I'd probably avoid it though. Merging two backups into one is convenient right up until that backup is compromised, and then it's two problems at once. Separate pieces of paper aren't that much extra effort.