package main import ( "crypto/x509" "database/sql" "encoding/pem" "fmt" "strings" "time" ) // ParseCertificateExtrakt Ablaufdatum und CA-Status aus einem PEM-Zertifikat // Gibt zurück: expiresAt, isIntermediate, error func ParseCertificate(certPEM string) (time.Time, bool, error) { block, _ := pem.Decode([]byte(certPEM)) if block == nil { return time.Time{}, false, fmt.Errorf("fehler beim Dekodieren des PEM-Blocks") } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { return time.Time{}, false, fmt.Errorf("fehler beim Parsen des Zertifikats: %v", err) } expiresAt := cert.NotAfter // Ein Zertifikat ist Intermediate wenn IsCA=true ist isIntermediate := cert.IsCA return expiresAt, isIntermediate, nil } // SplitCertificateChain trennt eine PEM-Zertifikatskette in einzelne Zertifikate // Gibt zurück: leafCert (PEM), intermediateCert (PEM), error func SplitCertificateChain(certChainPEM string) (string, string, error) { var leafCert string var intermediateCert string // Dekodiere alle PEM-Blöcke aus der Kette var blocks []*pem.Block rest := []byte(certChainPEM) for { block, remaining := pem.Decode(rest) if block == nil { break } if block.Type == "CERTIFICATE" { blocks = append(blocks, block) } rest = remaining } if len(blocks) == 0 { return "", "", fmt.Errorf("keine Zertifikate in der Kette gefunden") } // Parse jedes Zertifikat und trenne nach IsCA for _, block := range blocks { cert, err := x509.ParseCertificate(block.Bytes) if err != nil { continue // Überspringe ungültige Zertifikate } // Encode zurück zu PEM certPEM := string(pem.EncodeToMemory(block)) if cert.IsCA { // Intermediate CA if intermediateCert != "" { // Wenn bereits ein Intermediate vorhanden ist, hänge es an (kann mehrere geben) intermediateCert += "\n" + certPEM } else { intermediateCert = certPEM } } else { // Leaf Certificate if leafCert != "" { // Wenn bereits ein Leaf vorhanden ist, verwende das erste (sollte nur eines geben) continue } leafCert = certPEM } } return leafCert, intermediateCert, nil } // GetCertificateIssuer extrahiert den Issuer-Namen aus einem PEM-Zertifikat // Gibt zurück: issuerName (string), error func GetCertificateIssuer(certPEM string) (string, error) { block, _ := pem.Decode([]byte(certPEM)) if block == nil { return "", fmt.Errorf("fehler beim Dekodieren des PEM-Blocks") } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { return "", fmt.Errorf("fehler beim Parsen des Zertifikats: %v", err) } return cert.Issuer.String(), nil } // GetProviderNameFromIssuer bestimmt den Provider-Namen basierend auf dem Issuer func GetProviderNameFromIssuer(issuer string) string { issuerLower := fmt.Sprintf("%v", issuer) if strings.Contains(issuerLower, "Let's Encrypt") || strings.Contains(issuerLower, "letsencrypt") { return "Let's Encrypt" } if strings.Contains(issuerLower, "DigiCert") { return "DigiCert" } if strings.Contains(issuerLower, "GlobalSign") { return "GlobalSign" } if strings.Contains(issuerLower, "Sectigo") { return "Sectigo" } if strings.Contains(issuerLower, "GoDaddy") { return "GoDaddy" } // Fallback: Gib den Issuer-Namen zurück return issuer } // CheckExistingValidCertificate prüft ob bereits ein gültiges Zertifikat für einen FQDN existiert // Gibt zurück: exists (bool), expiresAt (time.Time), error func CheckExistingValidCertificate(fqdnID, spaceID string) (bool, time.Time, error) { var certPEM string var expiresAtStr sql.NullString err := db.QueryRow(` SELECT certificate_pem, expires_at FROM certificates WHERE fqdn_id = ? AND space_id = ? AND status = 'issued' ORDER BY created_at DESC LIMIT 1 `, fqdnID, spaceID).Scan(&certPEM, &expiresAtStr) if err != nil { if err == sql.ErrNoRows { return false, time.Time{}, nil } return false, time.Time{}, err } // Wenn expires_at bereits in DB vorhanden ist, verwende es if expiresAtStr.Valid && expiresAtStr.String != "" { expiresAt, err := time.Parse("2006-01-02 15:04:05", expiresAtStr.String) if err == nil { return true, expiresAt, nil } } // Sonst parse das Zertifikat expiresAt, _, err := ParseCertificate(certPEM) if err != nil { return true, time.Time{}, err } return true, expiresAt, nil }