From 5466e7620c3945b8bcd5f3d664fb176f2962adfb Mon Sep 17 00:00:00 2001 From: Alexander Lazarev Date: Wed, 15 Nov 2023 15:03:10 +0300 Subject: [PATCH] added crypt keys --- cmd/key/main.go | 45 ++++++++++++++++++++++++++++ internal/pkg/agent/config/config.go | 2 ++ internal/pkg/agent/encrypt.go | 32 ++++++++++++++++++++ internal/pkg/agent/reporter.go | 15 +++++++--- internal/pkg/agent/reporter_test.go | 3 +- internal/pkg/middleware/crypt.go | 43 ++++++++++++++++++++++++++ internal/pkg/server/config/config.go | 2 ++ internal/pkg/server/server.go | 1 + 8 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 cmd/key/main.go create mode 100644 internal/pkg/agent/encrypt.go diff --git a/cmd/key/main.go b/cmd/key/main.go new file mode 100644 index 0000000..ae41f4a --- /dev/null +++ b/cmd/key/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "log" + "os" +) + +func main() { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + log.Fatal(err) + } + + publicKey := &privateKey.PublicKey + privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey) + + privateKeyPEM := pem.EncodeToMemory(&pem.Block{ + Type: "SERVER PRIVATE KEY", + Bytes: privateKeyBytes, + }) + + err = os.WriteFile("./private.pem", privateKeyPEM, 0644) + if err != nil { + log.Fatal(err) + } + + publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + log.Fatal(err) + } + + publicKeyPEM := pem.EncodeToMemory(&pem.Block{ + Type: "AGENT PUBLIC KEY", + Bytes: publicKeyBytes, + }) + + err = os.WriteFile("./public.pem", publicKeyPEM, 0644) + if err != nil { + log.Fatal(err) + } +} diff --git a/internal/pkg/agent/config/config.go b/internal/pkg/agent/config/config.go index 28bcc56..2cd9c46 100644 --- a/internal/pkg/agent/config/config.go +++ b/internal/pkg/agent/config/config.go @@ -13,6 +13,7 @@ type AgentConfig struct { ReportInterval int `env:"REPORT_INTERVAL"` PollInterval int `env:"POLL_INTERVAL"` RateLimit int `env:"RATE_LIMIT"` + PublicKey string `env:"CRYPTO_KEY"` SignKeyByte []byte } @@ -44,5 +45,6 @@ func (c *AgentConfig) init() { flag.IntVar(&c.PollInterval, "p", pollIntervalDefault, "Interval of poll metric") flag.StringVar(&c.SignKey, "k", "", "Server key") flag.IntVar(&c.RateLimit, "l", rateLimitDefault, "Rate limit") + flag.StringVar(&c.PublicKey, "-crypto-key", "", "Public key path") flag.Parse() } diff --git a/internal/pkg/agent/encrypt.go b/internal/pkg/agent/encrypt.go new file mode 100644 index 0000000..c26e912 --- /dev/null +++ b/internal/pkg/agent/encrypt.go @@ -0,0 +1,32 @@ +package agent + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "os" +) + +func encrypt(publicKeyPath string, data []byte) ([]byte, error) { + publicKeyPEM, err := os.ReadFile(publicKeyPath) + if err != nil { + return nil, err + } + publicKeyBlock, _ := pem.Decode(publicKeyPEM) + if publicKeyBlock == nil { + return nil, err + } + + publicKey, err := x509.ParsePKIXPublicKey(publicKeyBlock.Bytes) + if err != nil { + return nil, err + } + + ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey.(*rsa.PublicKey), data) + if err != nil { + return nil, err + } + + return ciphertext, nil +} diff --git a/internal/pkg/agent/reporter.go b/internal/pkg/agent/reporter.go index 53ef4a6..3355794 100644 --- a/internal/pkg/agent/reporter.go +++ b/internal/pkg/agent/reporter.go @@ -24,7 +24,7 @@ func RunSendMetric(ctx context.Context, reportTicker *time.Ticker, c *config.Age case <-ctx.Done(): return case <-reportTicker.C: - ok, err := SendMetrics(ctx, s, c.ServerAddress, c.SignKeyByte) + ok, err := SendMetrics(ctx, s, c.ServerAddress, c.SignKeyByte, c.PublicKey) if err != nil { logrus.Errorf("Error send metrics %v", err) } @@ -37,7 +37,7 @@ func RunSendMetric(ctx context.Context, reportTicker *time.Ticker, c *config.Age } } -func SendMetrics(ctx context.Context, s storage.Store, serverAddress string, signKey []byte) (bool, error) { +func SendMetrics(ctx context.Context, s storage.Store, serverAddress string, signKey []byte, publicKey string) (bool, error) { metricsMap, err := s.GetMetrics(ctx) if err != nil { logrus.Errorf("Some error ocured during metrics get: %q", err) @@ -51,18 +51,25 @@ func SendMetrics(ctx context.Context, s storage.Store, serverAddress string, sig url := fmt.Sprintf("http://%s/updates/", serverAddress) - if err = SendBatchJSON(url, metricsBatch, signKey); err != nil { + if err = SendBatchJSON(url, metricsBatch, signKey, publicKey); err != nil { return false, fmt.Errorf("error create post request %w", err) } return true, nil } -func SendBatchJSON(url string, metricsBatch []*metrics.Metrics, signKey []byte) error { +func SendBatchJSON(url string, metricsBatch []*metrics.Metrics, signKey []byte, publicKey string) error { body, err := json.Marshal(metricsBatch) if err != nil { return fmt.Errorf("error encoding metric %w", err) } + if publicKey != "" { + body, err = encrypt(publicKey, body) + if err != nil { + return err + } + } + var buf bytes.Buffer gz := gzip.NewWriter(&buf) if _, err = gz.Write(body); err != nil { diff --git a/internal/pkg/agent/reporter_test.go b/internal/pkg/agent/reporter_test.go index dc9c3ab..853f241 100644 --- a/internal/pkg/agent/reporter_test.go +++ b/internal/pkg/agent/reporter_test.go @@ -23,6 +23,7 @@ const ( func TestSendReport(t *testing.T) { secretKey := []byte("secret") + publicKey := "" mtr := storage.NewMetrics() err := agent.UpdateMetrics(context.Background(), mtr) @@ -70,5 +71,5 @@ func TestSendReport(t *testing.T) { })) defer server.Close() - agent.SendMetrics(context.Background(), mtr, server.URL, secretKey) + agent.SendMetrics(context.Background(), mtr, server.URL, secretKey, publicKey) } diff --git a/internal/pkg/middleware/crypt.go b/internal/pkg/middleware/crypt.go index 68fe362..9d6b544 100644 --- a/internal/pkg/middleware/crypt.go +++ b/internal/pkg/middleware/crypt.go @@ -1,11 +1,17 @@ package middleware import ( + "bytes" "crypto/hmac" + "crypto/rand" + "crypto/rsa" "crypto/sha256" + "crypto/x509" "encoding/hex" + "encoding/pem" "io" "net/http" + "os" ) func CryptMiddleware(signKey []byte) func(handler http.Handler) http.Handler { @@ -51,3 +57,40 @@ func CryptMiddleware(signKey []byte) func(handler http.Handler) http.Handler { }) } } + +func DecryptMiddleware(privateKeyPath string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if privateKeyPath == "" { + return + } + + privateKeyPEM, err := os.ReadFile(privateKeyPath) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + privateKeyBlock, _ := pem.Decode(privateKeyPEM) + privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + r.Body = io.NopCloser(bytes.NewBuffer(plaintext)) + next.ServeHTTP(w, r) + }) + } +} diff --git a/internal/pkg/server/config/config.go b/internal/pkg/server/config/config.go index 05f0dfb..b3f66b9 100644 --- a/internal/pkg/server/config/config.go +++ b/internal/pkg/server/config/config.go @@ -14,6 +14,7 @@ type ServerConfig struct { DatabaseDSN string `env:"DATABASE_DSN"` StoreInterval int `env:"STORE_INTERVAL"` Restore bool `env:"RESTORE"` + PrivateKey string `env:"CRYPTO_KEY"` SignKeyByte []byte } @@ -45,5 +46,6 @@ func (c *ServerConfig) init() { flag.BoolVar(&c.Restore, "r", true, "Restore") flag.StringVar(&c.DatabaseDSN, "d", "", "Connect database string") flag.StringVar(&c.SignKey, "k", "", "Server key") + flag.StringVar(&c.PrivateKey, "-crypto-key", "", "Private key path") flag.Parse() } diff --git a/internal/pkg/server/server.go b/internal/pkg/server/server.go index 1a88051..ae03bc5 100644 --- a/internal/pkg/server/server.go +++ b/internal/pkg/server/server.go @@ -54,6 +54,7 @@ func StartListener(c *config.ServerConfig) { mux.Use( middleware.LoggingMiddleware, middleware.CryptMiddleware(c.SignKeyByte), + middleware.DecryptMiddleware(c.PrivateKey), ) RegisterHandlers(mux, metricStore)