Это старая версия документа!
SSH Phrase
Простая и безопасная утилита для генерации пары SSH-ключей (ED25519) по фразе.
Не сохраняет ключи, не использует сеть и не принимает аргументы — всё работает через интерактивное меню.
Подходит для защищённого доступа с зашифрованной флешки и персонального использования.
Программа в архиве:
sshphrase.zip (1.24 Mb)
Исходный код:
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 =====
- README.md
# 🔐 sshphrase: генерация SSH-ключей по фразе
## 📋 Назначение
`sshphrase.exe` — это автономная утилита для Windows, написанная на Go,
предназначенная для генерации пары SSH-ключей (ED25519) на основе вводимой пользователем фразы.
Программа не хранит ключи, не принимает аргументы и не требует доступа к сети.
---
## 🧠 Концепция
- 🔑 **Фраза пользователя** используется как единственный секрет
- 🧬 Генерация осуществляется через `SHA256(фраза)` → `ed25519.NewKeyFromSeed`
- 🧾 Публичный ключ можно использовать в `~/.ssh/authorized_keys`
- 🔒 Приватный ключ никогда не сохраняется автоматически
- 📎 Программа безопасна к публикации: исходный код открыт, безопасность держится на фразе
---
## ⚙️ Использование
1. Запусти `sshphrase.exe`
2. Введи фразу вручную (ввод скрыт, вставка недоступна)
3. Выбери действие:
- `1` — показать публичный ключ
- `2` — показать приватный ключ (в OpenSSH формате)
- `0` — выход
Скопируй нужный ключ в буфер и используй при настройке SSH-доступа.
---
## 📦 Примеры вывода
### Публичный ключ
```
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILvY... пользователь@фраза
```
### Приватный ключ (OpenSSH)
```
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktZ...много base64
-----END OPENSSH PRIVATE KEY-----
```
---
## 🛡️ Безопасность
- Нет аргументов CLI → невозможен автоматический перебор фраз
- Ввод фразы скрыт (`term.ReadPassword`) → безопасно от shoulder surfing
- Можно вручную добавлять "соль": имя устройства, дату, и т.п.
- Приватный ключ выводится только по запросу, без сохранения
- `.exe` можно запускать с зашифрованной флешки
---
## 💡 Рекомендации
- Используйте фразы длиной не менее 30 символов
- Добавляйте уникальные метки (например, `::server_name::2025`)
- Не храните приватный ключ, если не уверены в среде
- Не используйте одинаковую фразу на разных устройствах — добавляйте контекст
---
## 📁 Разработка
Программа написана на Go 1.20+
### Сборка
```
go build -ldflags="-s -w" -o sshphrase.exe
```
### Добавление иконки
```
rsrc -ico icon.ico
```
---
## 🧱 Лицензия и открытость
Исходный код открыт. Использование программы безопасно при соблюдении базовой цифровой гигиены.
---
**Автор:** [takraztak]
**Документация создана:** 2025-04-15