Files
knock-gui/back/cmd/decrypt.go
2025-08-17 00:43:58 +06:00

131 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cmd
import (
"encoding/base64"
"fmt"
"os"
"strings"
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"github.com/spf13/cobra"
)
var decryptCmd = &cobra.Command{
Use: "decrypt",
Short: "Расшифровать зашифрованный конфиг в открытый YAML",
Long: `Расшифровывает зашифрованный конфигурационный файл (ENCRYPTED:...) в обычный YAML-файл`,
PreRunE: func(cmd *cobra.Command, args []string) error {
// Для команды decrypt config не обязателен если есть -i
return nil
},
RunE: runDecrypt,
}
var (
decryptInputFile string
decryptOutputFile string
)
func init() {
rootCmd.AddCommand(decryptCmd)
decryptCmd.Flags().StringVarP(&decryptInputFile, "input", "i", "", "Входной зашифрованный файл (если не указан, используется --config)")
decryptCmd.Flags().StringVarP(&decryptOutputFile, "output", "o", "", "Выходной YAML-файл")
decryptCmd.MarkFlagRequired("output")
}
func runDecrypt(cmd *cobra.Command, args []string) error {
// Определяем входной файл: либо из -i, либо из глобального --config
input := decryptInputFile
if input == "" {
input = configFile
if input == "" {
return fmt.Errorf("необходимо указать входной файл через -i или --config")
}
}
data, err := os.ReadFile(input)
if err != nil {
return fmt.Errorf("не удалось прочитать входной файл %s: %w", input, err)
}
if !strings.HasPrefix(string(data), "ENCRYPTED:") {
return fmt.Errorf("файл %s не является зашифрованным (нет префикса ENCRYPTED:)", input)
}
key, err := getDecryptionKeyHashed(keyFile)
if err != nil {
return fmt.Errorf("не удалось получить ключ шифрования: %w", err)
}
decrypted, err := decryptData(data[10:], key)
if err != nil {
return fmt.Errorf("не удалось расшифровать данные: %w", err)
}
if err := os.WriteFile(decryptOutputFile, decrypted, 0600); err != nil {
return fmt.Errorf("не удалось записать YAML-файл: %w", err)
}
fmt.Printf("Файл успешно расшифрован: %s → %s\n", input, decryptOutputFile)
return nil
}
// getDecryptionKeyHashed получает ключ шифрования и хеширует его до 32 байт (аналогично encrypt)
func getDecryptionKeyHashed(keyFile string) ([]byte, error) {
var rawKey []byte
var err error
if keyFile != "" {
// Читаем ключ из файла
rawKey, err = os.ReadFile(keyFile)
if err != nil {
return nil, fmt.Errorf("не удалось прочитать файл ключа: %w", err)
}
} else {
// Пытаемся получить ключ из системной переменной
key := os.Getenv("GO_KNOCKER_SERVE_PASS")
if key == "" {
return nil, fmt.Errorf("ключ шифрования не найден ни в файле, ни в переменной GO_KNOCKER_SERVE_PASS")
}
rawKey = []byte(key)
}
// Хешируем ключ SHA256 чтобы получить всегда 32 байта
hash := sha256.Sum256(rawKey)
return hash[:], nil
}
// decryptData расшифровывает данные с помощью AES-GCM (аналогично internal)
func decryptData(encryptedData []byte, key []byte) ([]byte, error) {
data, err := base64.StdEncoding.DecodeString(string(encryptedData))
if err != nil {
return nil, fmt.Errorf("не удалось декодировать base64: %w", err)
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("не удалось создать AES cipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("не удалось создать GCM: %w", err)
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
return nil, fmt.Errorf("данные слишком короткие")
}
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("не удалось расшифровать: %w", err)
}
return plaintext, nil
}