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

128 lines
4.1 KiB
Go
Raw Permalink 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 (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"os"
"github.com/spf13/cobra"
)
var encryptCmd = &cobra.Command{
Use: "encrypt",
Short: "Зашифровать конфигурационный файл",
Long: `Зашифровывает YAML конфигурационный файл с помощью AES-GCM шифрования`,
PreRunE: func(cmd *cobra.Command, args []string) error {
// Для команды encrypt config не обязателен если есть -i
return nil
},
RunE: runEncrypt,
}
func init() {
rootCmd.AddCommand(encryptCmd)
encryptCmd.Flags().StringVarP(&inputFile, "input", "i", "", "Входной файл для шифрования (если не указан, используется --config)")
encryptCmd.Flags().StringVarP(&outputFile, "output", "o", "", "Выходной зашифрованный файл")
encryptCmd.MarkFlagRequired("output")
}
var (
inputFile string
outputFile string
)
func runEncrypt(cmd *cobra.Command, args []string) error {
// Определяем входной файл: либо из -i, либо из глобального --config
input := inputFile
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)
}
// Получаем ключ шифрования
key, err := getEncryptionKeyHashed(keyFile)
if err != nil {
return fmt.Errorf("не удалось получить ключ шифрования: %w", err)
}
// Шифруем данные
encryptedData, err := encrypt(data, key)
if err != nil {
return fmt.Errorf("не удалось зашифровать данные: %w", err)
}
// Записываем зашифрованный файл с префиксом "ENCRYPTED:"
output := "ENCRYPTED:" + encryptedData
if err := os.WriteFile(outputFile, []byte(output), 0600); err != nil {
return fmt.Errorf("не удалось записать зашифрованный файл: %w", err)
}
fmt.Printf("Файл успешно зашифрован: %s → %s\n", input, outputFile)
return nil
}
// getEncryptionKeyHashed получает ключ шифрования и хеширует его до 32 байт
func getEncryptionKeyHashed(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
}
// encrypt шифрует данные с помощью AES-GCM
func encrypt(plaintext []byte, key []byte) (string, error) {
// Создаем AES cipher
block, err := aes.NewCipher(key)
if err != nil {
return "", fmt.Errorf("не удалось создать AES cipher: %w", err)
}
// Создаем GCM
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("не удалось создать GCM: %w", err)
}
// Создаем nonce
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return "", fmt.Errorf("не удалось создать nonce: %w", err)
}
// Шифруем
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
// Кодируем в base64
return base64.StdEncoding.EncodeToString(ciphertext), nil
}