250 lines
7.8 KiB
Go
250 lines
7.8 KiB
Go
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")
|
|
}
|