projects:windows:ssh_phrase
Различия
Показаны различия между двумя версиями страницы.
Предыдущая версия справа и слеваПредыдущая версияСледующая версия | Предыдущая версия | ||
projects:windows:ssh_phrase [2025/04/15 18:31] – | projects:windows:ssh_phrase [2025/04/15 23:01] (текущий) – | ||
---|---|---|---|
Строка 5: | Строка 5: | ||
Подходит для защищённого доступа с зашифрованной флешки и персонального использования.**// | Подходит для защищённого доступа с зашифрованной флешки и персонального использования.**// | ||
- | {{ : | + | **Программа в архиве: |
+ | {{ : | ||
- | **Исходный код:**\\ | + | |
+ | <WRAP center round important 90%> | ||
+ | < | ||
+ | в ssh приватном ключе вида | ||
+ | -----BEGIN OPENSSH PRIVATE KEY----- | ||
+ | b3BlbnNzaC1rZXktZ...много base64 | ||
+ | -----END OPENSSH PRIVATE KEY----- | ||
+ | обязательно должен быть символ переноса на новую строку в конце файла, | ||
+ | иначе будет ошибка при попытке подключения | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | **Исходный код:** | ||
++++sshphrase.go| | ++++sshphrase.go| | ||
<code go sshphrase.go> | <code go sshphrase.go> | ||
- | // sshphrase.go | + | // sshphrase.go |
package main | package main | ||
Строка 26: | Строка 41: | ||
) | ) | ||
- | // ===== marshalOpenSSHPrivateKey: | + | type Pair struct { |
- | func marshalOpenSSHPrivateKey(privateKey ed25519.PrivateKey) ([]byte, error) { | + | Private |
- | // Структура для OpenSSH-ключа | + | Public |
- | type opensshPrivateKey | + | } |
- | Check1 uint32 | + | |
- | Check2 uint32 | + | |
- | Key struct { | + | |
- | Curve | + | |
- | PubKey | + | |
- | Priv | + | |
- | } | + | |
- | } | + | |
- | pub := privateKey.Public().(ed25519.PublicKey) | + | func PairFromED25519(public |
- | buf := &bytes.Buffer{} | + | privBlock, err := ssh.MarshalPrivateKey(private, "" |
- | buf.Write([]byte(" | + | if err != nil { |
- | writeString(buf, " | + | return nil, err |
- | writeString(buf, "none") // kdf name (без ключевого деривационного алгоритма) | + | } |
- | writeString(buf, "" | + | |
- | writeUint32(buf, | + | |
- | pubKey, | + | var buf bytes.Buffer |
+ | err = pem.Encode(&buf, privBlock) | ||
if err != nil { | if err != nil { | ||
return nil, err | return nil, err | ||
} | } | ||
- | writeString(buf, | ||
- | content | + | pubKey, err := ssh.NewPublicKey(public) |
- | check := uint32(0x01020304) // контрольное значение для валидации | + | if err != nil { |
- | writeUint32(content, | + | return nil, err |
- | writeUint32(content, | + | |
- | writeString(content, | + | |
- | writeString(content, | + | |
- | writeString(content, | + | |
- | content.WriteByte(0) // завершающий ноль | + | |
- | + | ||
- | padLen := 8 - (content.Len() % 8) | + | |
- | for i := 1; i <= padLen; i++ { | + | |
- | content.WriteByte(byte(i)) | + | |
} | } | ||
- | writeString(buf, | ||
- | final := pem.EncodeToMemory(&pem.Block{ | + | return |
- | Type: " | + | Private: buf.Bytes(), |
- | Bytes: buf.Bytes(), | + | Public: ssh.MarshalAuthorizedKey(pubKey), |
- | }) | + | }, nil |
- | return final, nil | + | |
} | } | ||
- | // ===== Конец marshalOpenSSHPrivateKey ===== | ||
- | |||
- | func writeString(w *bytes.Buffer, | ||
- | writeUint32(w, | ||
- | w.WriteString(s) | ||
- | } | ||
- | |||
- | func writeUint32(w *bytes.Buffer, | ||
- | w.WriteByte(byte(v >> 24)) | ||
- | w.WriteByte(byte(v >> 16)) | ||
- | w.WriteByte(byte(v >> 8)) | ||
- | w.WriteByte(byte(v)) | ||
- | } | ||
- | |||
- | // ===== main: CLI-меню, | ||
func main() { | func main() { | ||
fmt.Print(" | fmt.Print(" | ||
Строка 99: | Строка 78: | ||
phrase := string(b) | phrase := string(b) | ||
- | // Создание seed на основе хэша введённой фразы | ||
seed := sha256.Sum256([]byte(phrase)) | seed := sha256.Sum256([]byte(phrase)) | ||
privateKey := ed25519.NewKeyFromSeed(seed[: | privateKey := ed25519.NewKeyFromSeed(seed[: | ||
- | publicKey, err := ssh.NewPublicKey(privateKey.Public()) | + | publicKey := privateKey.Public().(ed25519.PublicKey) |
+ | |||
+ | pair, err := PairFromED25519(publicKey, | ||
if err != nil { | if err != nil { | ||
- | log.Fatalf(" | + | log.Fatalf(" |
} | } | ||
Строка 120: | Строка 100: | ||
case " | case " | ||
fmt.Println(" | fmt.Println(" | ||
- | fmt.Println(string(ssh.MarshalAuthorizedKey(publicKey))) | + | fmt.Println(string(pair.Public)) |
case " | case " | ||
fmt.Println(" | fmt.Println(" | ||
- | keyBytes, err := marshalOpenSSHPrivateKey(privateKey) | + | fmt.Println(string(pair.Private)) |
- | if err != nil { | + | |
- | log.Fatalf(" | + | |
- | } | + | |
- | fmt.Println(string(keyBytes)) | + | |
fmt.Println(" | fmt.Println(" | ||
Строка 141: | Строка 117: | ||
} | } | ||
- | // ===== Конец main ===== | + | </code> |
+ | ++++ | ||
+ | <code bash README.md> | ||
+ | # 🔐 sshphrase: генерация SSH-ключей по фразе | ||
+ | ## 📋 Назначение | ||
+ | |||
+ | `sshphrase.exe` — это автономная утилита для Windows, написанная на Go, | ||
+ | предназначенная для генерации пары SSH-ключей (ED25519) на основе вводимой пользователем фразы. | ||
+ | Программа не хранит ключи, не принимает аргументы и не требует доступа к сети. | ||
+ | |||
+ | --- | ||
+ | |||
+ | ## 🧠 Концепция | ||
+ | |||
+ | - 🔑 **Фраза пользователя** используется как единственный секрет | ||
+ | - 🧬 Генерация осуществляется через `SHA256(фраза)` → `ed25519.NewKeyFromSeed` | ||
+ | - 🧾 Публичный ключ можно использовать в `~/ | ||
+ | - 🔒 Приватный ключ никогда не сохраняется автоматически | ||
+ | - 📎 Программа безопасна к публикации: | ||
+ | |||
+ | --- | ||
+ | |||
+ | ## ⚙️ Использование | ||
+ | |||
+ | 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 символов | ||
+ | - Добавляйте уникальные метки (например, | ||
+ | - Не храните приватный ключ, если не уверены в среде | ||
+ | - Не используйте одинаковую фразу на разных устройствах — добавляйте контекст | ||
+ | |||
+ | --- | ||
+ | |||
+ | ## 📁 Разработка | ||
+ | |||
+ | Программа написана на Go 1.20+ | ||
+ | |||
+ | ### Сборка | ||
+ | ``` | ||
+ | go build -ldflags=" | ||
+ | ``` | ||
+ | |||
+ | ### Добавление иконки | ||
+ | ``` | ||
+ | rsrc -ico icon.ico | ||
+ | ``` | ||
+ | |||
+ | --- | ||
+ | |||
+ | ## 🧱 Лицензия и открытость | ||
+ | |||
+ | Исходный код открыт. Использование программы безопасно при соблюдении базовой цифровой гигиены. | ||
+ | |||
+ | --- | ||
+ | |||
+ | **Автор: | ||
+ | **Документация создана: | ||
</ | </ | ||
- | ++++ | ||
projects/windows/ssh_phrase.1744731078.txt.gz · Последнее изменение: 2025/04/15 18:31 —