- Kompletna Go implementacija licencnog servera (19 Go fajlova) - Klijentski API: activate, deactivate, validate - Admin API: CRUD licence, stats, audit log - Admin dashboard: htmx + Go templates - RSA-2048 potpisivanje licencnih podataka - Rate limiting i API key autentifikacija - MySQL migracije i seed podaci (ESIR, ARV, LIGHT_TICKET) - Unit testovi: keygen, crypto, model, middleware (24 testa) - Dokumentacija: SPEC.md, ARCHITECTURE.md, SETUP.md, API.md, TESTING.md, README.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
64 lines
1.4 KiB
Go
64 lines
1.4 KiB
Go
package service
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
type CryptoService struct {
|
|
privateKey *rsa.PrivateKey
|
|
}
|
|
|
|
func NewCryptoService(keyPath string) (*CryptoService, error) {
|
|
data, err := os.ReadFile(keyPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read private key: %w", err)
|
|
}
|
|
|
|
block, _ := pem.Decode(data)
|
|
if block == nil {
|
|
return nil, fmt.Errorf("failed to decode PEM block")
|
|
}
|
|
|
|
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
if err != nil {
|
|
// Try PKCS8
|
|
pkcs8Key, err2 := x509.ParsePKCS8PrivateKey(block.Bytes)
|
|
if err2 != nil {
|
|
return nil, fmt.Errorf("parse private key: %w", err)
|
|
}
|
|
var ok bool
|
|
key, ok = pkcs8Key.(*rsa.PrivateKey)
|
|
if !ok {
|
|
return nil, fmt.Errorf("not an RSA private key")
|
|
}
|
|
}
|
|
|
|
return &CryptoService{privateKey: key}, nil
|
|
}
|
|
|
|
func (s *CryptoService) Sign(data []byte) (string, error) {
|
|
hash := sha256.Sum256(data)
|
|
sig, err := rsa.SignPKCS1v15(rand.Reader, s.privateKey, crypto.SHA256, hash[:])
|
|
if err != nil {
|
|
return "", fmt.Errorf("sign: %w", err)
|
|
}
|
|
return "RSA-SHA256:" + base64.StdEncoding.EncodeToString(sig), nil
|
|
}
|
|
|
|
func (s *CryptoService) PublicKeyPEM() (string, error) {
|
|
pubBytes, err := x509.MarshalPKIXPublicKey(&s.privateKey.PublicKey)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
block := &pem.Block{Type: "PUBLIC KEY", Bytes: pubBytes}
|
|
return string(pem.EncodeToMemory(block)), nil
|
|
}
|