package ssl import ( "context" "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "fmt" "github.com/google/uuid" "math/big" "time" "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") }