# barox Zertifikats-Generator

Internes Web-Tool zur Erzeugung von HTTPS-Zertifikaten für Netzwerk-Switche.
Mitarbeiter melden sich per **Microsoft 365 / Azure AD** an, füllen ein Formular aus
und erhalten eine fertige **`switch.pem`** (Zertifikat + Private Key), die direkt im
Switch-Webinterface für HTTPS hochgeladen werden kann.

---

## Funktionen

- 🔐 Login über Microsoft 365 / Azure AD (OAuth2 Authorization Code Flow)
- 📝 Formular für CN, O, OU, C, ST, L, Gültigkeit, Schlüssellänge und SANs (DNS + IP)
- 🔑 Erzeugt **Private Key**, **CSR**, **selbstsigniertes Zertifikat** und kombinierte **PEM**
- 🔄 Private-Key-Format wählbar: **PKCS#1** (`BEGIN RSA PRIVATE KEY`) oder **PKCS#8** (`BEGIN PRIVATE KEY`)
- ⬇️ Downloads: `switch.pem` (Pflicht) sowie optional `switch.crt`, `switch.key`, `switch.csr`
- 🧾 Optionales Audit-Log (wer, wann, CN, SANs, Gültigkeit – **niemals** der Private Key)
- 🧹 Private Keys werden **nie dauerhaft** gespeichert; temporäre Dateien liegen in einem
  mit `0700` geschützten Ordner und werden automatisch gelöscht (TTL + Cron + bei jedem Request)

> **Hinweis zum Key-Format:** Switche **unterhalb der 28er-Serie benötigen zwingend PKCS#1**.
> Für neuere Serien kann PKCS#8 verwendet werden.

---

## Projektstruktur

```
barox-cert/
├── public/                 ← DocumentRoot von Apache
│   ├── index.php           Formular (nur eingeloggt)
│   ├── generate.php        POST-Handler: Validierung + Generierung
│   ├── result.php          Ergebnis-/Download-Seite + Anleitung
│   ├── download.php        Liefert Dateien aus (nur eingeloggt)
│   ├── delete.php          Sofortiges Löschen der Job-Dateien
│   ├── login.php           Login-Startseite
│   ├── callback.php        Azure-AD-Redirect-Handler
│   ├── logout.php
│   ├── .htaccess           Security-Header, Indexes aus
│   └── assets/             css / fonts / img (Logo, Favicon)
├── src/                    Anwendungslogik (Namespace Barox\Cert)
│   ├── bootstrap.php       Autoloader, Session, GC
│   ├── Config.php          Konfigurations-Loader
│   ├── Session.php         Session + CSRF
│   ├── Auth.php            Azure AD / M365 OAuth2
│   ├── Validator.php       Strenge Eingabevalidierung
│   ├── CertGenerator.php   OpenSSL-Aufrufe (ohne Shell)
│   ├── Storage.php         Geschützter Temp-Speicher + sicheres Löschen + GC
│   └── Audit.php           Audit-Log (ohne Private Key)
├── templates/              header / footer / form
├── config/
│   ├── config.example.php  Vorlage – kopieren nach config/config.php
│   └── config.php          (lokal, NICHT eingecheckt, enthält Secrets)
├── storage/
│   ├── tmp/                ← temporäre Job-Ordner (0700), automatisch geleert
│   ├── audit.log           ← Audit-Log (0600)
│   └── .htaccess           Require all denied (Defense in depth)
├── bin/gc.php              Garbage Collection für Cron
└── README.md
```

---

## Sicherheitskonzept

| Anforderung | Umsetzung |
|---|---|
| Private Keys nie dauerhaft speichern | Nur im pro-Job-Ordner unter `storage/tmp/<hex>` (0700); per TTL/Cron/GC gelöscht |
| Dateien nach Download löschen | Auto-Löschung per TTL (Standard 10 min) + Button „Dateien jetzt löschen“ |
| Geschützter Temp-Ordner | `storage/` außerhalb des DocumentRoot, zusätzlich `.htaccess`-Sperre, Mode 0700 |
| Keine Shell-Injection | OpenSSL wird über `proc_open` mit **Argument-Array** aufgerufen → keine Shell |
| Eingaben validieren/escapen | `Validator` (Whitelist-Zeichen, Hostname-/IP-/Ländercode-Prüfung), HTML-Escaping in der UI |
| Nur eingeloggte Benutzer | `Auth::requireLogin()` auf jeder geschützten Seite; CSRF-Token bei jedem POST |
| Audit-Log ohne Private Key | `Audit` protokolliert nur Metadaten als JSON-Zeilen |
| Path-Traversal | Job-Token nur `[a-f0-9]{32}`, Dateinamen-Whitelist, `realpath`-Prüfung |

---

## Installation auf Ubuntu Server (Apache + PHP)

### 1. Pakete installieren

```bash
sudo apt update
sudo apt install -y apache2 php php-curl php-mbstring openssl libapache2-mod-php
sudo a2enmod headers rewrite
```

> `openssl` und `php-curl` sind zwingend erforderlich, `php-mbstring` wird empfohlen.

### 2. Projekt ablegen

```bash
sudo mkdir -p /var/www/barox-cert
sudo cp -r ./barox-cert/* /var/www/barox-cert/
```

### 3. Konfiguration anlegen

```bash
cd /var/www/barox-cert
sudo cp config/config.example.php config/config.php
sudo nano config/config.php   # Azure-AD-Daten, base_url, openssl_bin etc.
```

Wichtige Werte:
- `base_url` – z. B. `https://cert.barox.local`
- `openssl_bin` – auf Ubuntu i. d. R. `/usr/bin/openssl`
- `azure.tenant_id`, `azure.client_id`, `azure.client_secret`, `azure.redirect_uri`
- `auth_mode` = `azuread` (für `dev` siehe unten)

### 4. Rechte setzen

```bash
sudo chown -R www-data:www-data /var/www/barox-cert
sudo chmod 750 /var/www/barox-cert
sudo chmod 700 /var/www/barox-cert/storage /var/www/barox-cert/storage/tmp
sudo chmod 600 /var/www/barox-cert/config/config.php
```

### 5. Apache vHost (DocumentRoot = `public/`)

`/etc/apache2/sites-available/barox-cert.conf`:

```apache
<VirtualHost *:443>
    ServerName cert.barox.local
    DocumentRoot /var/www/barox-cert/public

    SSLEngine on
    SSLCertificateFile      /etc/ssl/certs/cert.barox.local.crt
    SSLCertificateKeyFile   /etc/ssl/private/cert.barox.local.key

    <Directory /var/www/barox-cert/public>
        AllowOverride All
        Require all granted
        Options -Indexes +FollowSymLinks
    </Directory>

    # storage/, src/, config/ liegen außerhalb von DocumentRoot und sind
    # damit nicht über das Web erreichbar.

    ErrorLog  ${APACHE_LOG_DIR}/barox-cert-error.log
    CustomLog ${APACHE_LOG_DIR}/barox-cert-access.log combined
</VirtualHost>

# HTTP -> HTTPS Weiterleitung
<VirtualHost *:80>
    ServerName cert.barox.local
    Redirect permanent / https://cert.barox.local/
</VirtualHost>
```

```bash
sudo a2enmod ssl
sudo a2ensite barox-cert
sudo systemctl reload apache2
```

> **Wichtig:** Das Tool sollte ausschließlich über **HTTPS** betrieben werden
> (Secure-Cookies, Token-Übertragung). Für ein internes Tool genügt ein eigenes
> Zertifikat einer internen CA.

### 6. Garbage Collection per Cron (empfohlen)

```bash
sudo crontab -u www-data -e
# minütlich alte Job-Dateien entfernen:
* * * * * /usr/bin/php /var/www/barox-cert/bin/gc.php >/dev/null 2>&1
```

---

## Azure AD / Entra ID – App-Registrierung

1. **Entra ID → App-Registrierungen → Neue Registrierung**
2. Name: `barox Zertifikats-Generator`
3. **Redirect-URI** (Typ *Web*): `https://cert.barox.local/callback.php`
4. **Zertifikate & Geheimnisse → Neuer geheimer Clientschlüssel** → Wert als
   `azure.client_secret` eintragen.
5. **API-Berechtigungen:** `openid`, `profile`, `email` (Microsoft Graph, delegiert).
6. `tenant_id` (Verzeichnis-ID) und `client_id` (Anwendungs-ID) in `config/config.php` eintragen.
7. Optional in `config.php` unter `azure.allowed_domains` die zugelassenen
   E-Mail-Domains einschränken.

---

## Lokales Testen ohne Azure AD

In `config/config.php`:

```php
'auth_mode' => 'dev',   // NUR für Tests – niemals in Produktion!
```

Damit erfolgt die Anmeldung ohne Microsoft 365 mit dem unter `dev_user` definierten Benutzer.

---

## Bedienung

1. Anmelden (Microsoft 365).
2. Formular ausfüllen – mindestens **CN**, **O**, **C**, **Gültigkeit**, **Key-Länge** und **Key-Format**.
3. SANs (DNS-Namen und/oder IP-Adressen) eintragen. Der CN wird automatisch als SAN ergänzt.
4. **„Zertifikat generieren“** klicken.
5. **`switch.pem`** herunterladen (optional zusätzlich `.crt`, `.key`, `.csr`).
6. Im Switch-Webinterface unter HTTPS/SSL die `switch.pem` hochladen.

Die finale `switch.pem` enthält Zertifikat **und** Private Key im gewählten Format
(entspricht `cat switch.crt switch.key > switch.pem`):

```
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----        (PKCS#8)   bzw.
-----BEGIN RSA PRIVATE KEY-----    (PKCS#1)
...
-----END ... PRIVATE KEY-----
```

---

## Optional: MySQL-Audit-Log

Standardmäßig wird das Audit-Log als JSON-Datei (`storage/audit.log`) geführt – das ist
für ein internes Tool ausreichend und benötigt keine Datenbank. Wer ein zentrales,
abfragbares Log möchte, kann `src/Audit.php` auf eine MySQL-Tabelle umstellen
(Felder: `ts, user, ip, cn, o, ou, c, st, l, days, bits, key_format, san_dns, san_ip`).
**Der Private Key darf auch dort niemals gespeichert werden.**

---

## Technische Hinweise

- Keine externen PHP-Abhängigkeiten / kein Composer nötig – einfacher PSR-4-Autoloader.
- OpenSSL-Aufrufe erfolgen ausschließlich über `proc_open` mit Argument-Arrays
  (kein `/bin/sh`), wodurch Shell-Injection ausgeschlossen ist.
- Getestet mit OpenSSL 3.x (PKCS#1 via `rsa -traditional`, mit Fallback für OpenSSL 1.1.x)
  und OpenSSL 1.1.x.
