Это старая версия документа!
SSH Phrase
Простая и безопасная утилита для генерации пары SSH-ключей (ED25519) по фразе.
Не сохраняет ключи, не использует сеть и не принимает аргументы — всё работает через интерактивное меню.
Подходит для защищённого доступа с зашифрованной флешки и персонального использования.
Данный код позволяет указывать фразу из которой будет сгенерирован ключ OpenSSH
Исходный код:
sshphrase.go
- sshphrase.go
// sshphrase.go
package main
import (
"bytes"
"crypto/ed25519"
"crypto/sha256"
"encoding/pem"
"fmt"
"log"
"os"
"golang.org/x/crypto/ssh"
"golang.org/x/term"
)
// ===== marshalOpenSSHPrivateKey: Сериализация приватного ключа в OpenSSH-формате =====
func marshalOpenSSHPrivateKey(privateKey ed25519.PrivateKey) ([]byte, error) {
// Структура для OpenSSH-ключа
type opensshPrivateKey struct {
Check1 uint32
Check2 uint32
Key struct {
Curve string
PubKey []byte
Priv []byte
}
}
pub := privateKey.Public().(ed25519.PublicKey)
buf := &bytes.Buffer{}
buf.Write([]byte("openssh-key-v1\x00"))
writeString(buf, "none") // cipher name (без шифрования)
writeString(buf, "none") // kdf name (без ключевого деривационного алгоритма)
writeString(buf, "") // kdf options (пусто)
writeUint32(buf, 1) // количество ключей
pubKey, err := ssh.NewPublicKey(pub)
if err != nil {
return nil, err
}
writeString(buf, string(ssh.MarshalAuthorizedKey(pubKey)))
content := &bytes.Buffer{}
check := uint32(0x01020304) // контрольное значение для валидации
writeUint32(content, check)
writeUint32(content, check)
writeString(content, "ssh-ed25519")
writeString(content, string(pub))
writeString(content, string(privateKey))
content.WriteByte(0) // завершающий ноль
padLen := 8 - (content.Len() % 8)
for i := 1; i <= padLen; i++ {
content.WriteByte(byte(i))
}
writeString(buf, content.String())
final := pem.EncodeToMemory(&pem.Block{
Type: "OPENSSH PRIVATE KEY",
Bytes: buf.Bytes(),
})
return final, nil
}
// ===== Конец marshalOpenSSHPrivateKey =====
func writeString(w *bytes.Buffer, s string) {
writeUint32(w, uint32(len(s)))
w.WriteString(s)
}
func writeUint32(w *bytes.Buffer, v uint32) {
w.WriteByte(byte(v >> 24))
w.WriteByte(byte(v >> 16))
w.WriteByte(byte(v >> 8))
w.WriteByte(byte(v))
}
// ===== main: CLI-меню, генерация ключей по введённой фразе =====
func main() {
fmt.Print("Введите фразу: ")
b, err := term.ReadPassword(int(os.Stdin.Fd()))
fmt.Println()
if err != nil {
log.Fatalf("Ошибка чтения фразы: %v", err)
}
phrase := string(b)
// Создание seed на основе хэша введённой фразы
seed := sha256.Sum256([]byte(phrase))
privateKey := ed25519.NewKeyFromSeed(seed[:])
publicKey, err := ssh.NewPublicKey(privateKey.Public())
if err != nil {
log.Fatalf("Ошибка создания публичного ключа: %v", err)
}
for {
fmt.Println("\nВыберите действие:")
fmt.Println("1 - Показать публичный ключ")
fmt.Println("2 - Показать приватный ключ")
fmt.Println("0 - Выход")
fmt.Print("> ")
var choice string
fmt.Scanln(&choice)
switch choice {
case "1":
fmt.Println("\n📤 Публичный ключ (копируй в authorized_keys):")
fmt.Println(string(ssh.MarshalAuthorizedKey(publicKey)))
case "2":
fmt.Println("\n🔐 Приватный ключ (в формате OpenSSH):")
keyBytes, err := marshalOpenSSHPrivateKey(privateKey)
if err != nil {
log.Fatalf("Ошибка сериализации приватного ключа: %v", err)
}
fmt.Println(string(keyBytes))
fmt.Println("(Сохрани как id_ed25519 и установи chmod 600)")
case "0":
fmt.Println("Выход.")
return
default:
fmt.Println("Неверный выбор. Попробуйте снова.")
}
}
}
// ===== Конец main =====