veza/veza-backend-api/internal/infrastructure/ssl/providers.go

252 lines
7.8 KiB
Go
Raw Normal View History

2025-12-03 19:29:37 +00:00
package ssl
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"time"
2026-03-05 22:03:43 +00:00
"github.com/google/uuid"
2025-12-03 19:29:37 +00:00
"go.uber.org/zap"
)
// LetsEncryptProvider provider pour Let's Encrypt
type LetsEncryptProvider struct {
logger *zap.Logger
config LetsEncryptConfig
}
// LetsEncryptConfig configuration Let's Encrypt
type LetsEncryptConfig struct {
Endpoint string `yaml:"endpoint"`
Email string `yaml:"email"`
KeySize int `yaml:"key_size"`
}
// SelfSignedProvider provider pour certificats auto-signés
type SelfSignedProvider struct {
logger *zap.Logger
config SelfSignedConfig
}
// SelfSignedConfig configuration auto-signés
type SelfSignedConfig struct {
KeySize int `yaml:"key_size"`
ValidDuration time.Duration `yaml:"valid_duration"`
Organization string `yaml:"organization"`
Country string `yaml:"country"`
}
// NewLetsEncryptProvider crée un nouveau provider Let's Encrypt
func NewLetsEncryptProvider(logger *zap.Logger) *LetsEncryptProvider {
return &LetsEncryptProvider{
logger: logger,
config: LetsEncryptConfig{
Endpoint: "https://acme-v02.api.letsencrypt.org/directory",
KeySize: 2048,
},
}
}
// NewSelfSignedProvider crée un nouveau provider auto-signé
func NewSelfSignedProvider(logger *zap.Logger) *SelfSignedProvider {
return &SelfSignedProvider{
logger: logger,
config: SelfSignedConfig{
KeySize: 2048,
ValidDuration: 365 * 24 * time.Hour, // 1 an
Organization: "Veza Platform",
Country: "US",
},
}
}
// LetsEncryptProvider implementation
func (lep *LetsEncryptProvider) GenerateCertificate(ctx context.Context, domain string, aliases []string, contact string) (*Certificate, error) {
lep.logger.Info("Generating Let's Encrypt certificate", zap.String("domain", domain))
// Simulation de génération avec Let's Encrypt
// En production, utiliser une librairie comme golang.org/x/crypto/acme
cert := &Certificate{
ID: fmt.Sprintf("le_%s_%d", domain, uuid.New()),
Domain: domain,
Aliases: aliases,
Provider: "letsencrypt",
Status: CertStatusValid,
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(90 * 24 * time.Hour), // Let's Encrypt: 90 jours
LastChecked: time.Now(),
AutoRenew: true,
Contact: contact,
Metadata: map[string]interface{}{
"issuer": "Let's Encrypt Authority X3",
"key_size": lep.config.KeySize,
},
}
lep.logger.Info("Let's Encrypt certificate generated", zap.String("domain", domain))
return cert, nil
}
func (lep *LetsEncryptProvider) RenewCertificate(ctx context.Context, cert *Certificate) (*Certificate, error) {
lep.logger.Info("Renewing Let's Encrypt certificate", zap.String("domain", cert.Domain))
// Simulation de renouvellement
newCert := &Certificate{
ID: fmt.Sprintf("le_%s_%d", cert.Domain, uuid.New()),
Domain: cert.Domain,
Aliases: cert.Aliases,
Provider: "letsencrypt",
Status: CertStatusValid,
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(90 * 24 * time.Hour), // 90 jours
LastChecked: time.Now(),
AutoRenew: cert.AutoRenew,
Contact: cert.Contact,
Metadata: map[string]interface{}{
"issuer": "Let's Encrypt Authority X3",
"key_size": lep.config.KeySize,
"renewed_from": cert.ID,
},
}
lep.logger.Info("Let's Encrypt certificate renewed", zap.String("domain", cert.Domain))
return newCert, nil
}
func (lep *LetsEncryptProvider) RevokeCertificate(ctx context.Context, cert *Certificate) error {
lep.logger.Info("Revoking Let's Encrypt certificate", zap.String("domain", cert.Domain))
// Simulation de révocation
// En production, utiliser l'API ACME pour révoquer
lep.logger.Info("Let's Encrypt certificate revoked", zap.String("domain", cert.Domain))
return nil
}
func (lep *LetsEncryptProvider) ValidateCertificate(ctx context.Context, cert *Certificate) error {
if cert.Provider != "letsencrypt" {
return fmt.Errorf("certificate is not from Let's Encrypt")
}
if time.Until(cert.ExpiresAt) <= 0 {
return fmt.Errorf("certificate has expired")
}
return nil
}
func (lep *LetsEncryptProvider) GetCertificateInfo(ctx context.Context, domain string) (*Certificate, error) {
// Simulation de récupération d'info
return nil, fmt.Errorf("certificate info not available")
}
// SelfSignedProvider implementation
func (ssp *SelfSignedProvider) GenerateCertificate(ctx context.Context, domain string, aliases []string, contact string) (*Certificate, error) {
ssp.logger.Info("Generating self-signed certificate", zap.String("domain", domain))
// Générer une clé privée
privateKey, err := rsa.GenerateKey(rand.Reader, ssp.config.KeySize)
if err != nil {
return nil, fmt.Errorf("failed to generate private key: %w", err)
}
// Créer le template de certificat
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{ssp.config.Organization},
Country: []string{ssp.config.Country},
Province: []string{""},
Locality: []string{""},
StreetAddress: []string{""},
PostalCode: []string{""},
CommonName: domain,
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(ssp.config.ValidDuration),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
DNSNames: append([]string{domain}, aliases...),
}
// Générer le certificat
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
if err != nil {
return nil, fmt.Errorf("failed to create certificate: %w", err)
}
// Parser le certificat
x509Cert, err := x509.ParseCertificate(certDER)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate: %w", err)
}
cert := &Certificate{
ID: fmt.Sprintf("ss_%s_%d", domain, uuid.New()),
Domain: domain,
Aliases: aliases,
Provider: "self-signed",
Certificate: x509Cert,
PrivateKey: privateKey,
PEMData: certDER,
Status: CertStatusValid,
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(ssp.config.ValidDuration),
LastChecked: time.Now(),
AutoRenew: false, // Auto-renew désactivé par défaut pour auto-signé
Contact: contact,
Metadata: map[string]interface{}{
"issuer": "Self-Signed",
"key_size": ssp.config.KeySize,
"algorithm": "RSA",
"self_signed": true,
},
}
ssp.logger.Info("Self-signed certificate generated", zap.String("domain", domain))
return cert, nil
}
func (ssp *SelfSignedProvider) RenewCertificate(ctx context.Context, cert *Certificate) (*Certificate, error) {
ssp.logger.Info("Renewing self-signed certificate", zap.String("domain", cert.Domain))
// Pour auto-signé, on génère un nouveau certificat
return ssp.GenerateCertificate(ctx, cert.Domain, cert.Aliases, cert.Contact)
}
func (ssp *SelfSignedProvider) RevokeCertificate(ctx context.Context, cert *Certificate) error {
ssp.logger.Info("Revoking self-signed certificate", zap.String("domain", cert.Domain))
// Pour auto-signé, pas de révocation réelle nécessaire
// Juste marquer comme révoqué
ssp.logger.Info("Self-signed certificate revoked", zap.String("domain", cert.Domain))
return nil
}
func (ssp *SelfSignedProvider) ValidateCertificate(ctx context.Context, cert *Certificate) error {
if cert.Provider != "self-signed" {
return fmt.Errorf("certificate is not self-signed")
}
if time.Until(cert.ExpiresAt) <= 0 {
return fmt.Errorf("certificate has expired")
}
return nil
}
func (ssp *SelfSignedProvider) GetCertificateInfo(ctx context.Context, domain string) (*Certificate, error) {
// Simulation de récupération d'info
return nil, fmt.Errorf("certificate info not available for self-signed")
}