====== Zertifikats-Inventur ======
**Komplexität:** Niedrig \\
**Dauer:** 1-4 Stunden (je nach Größe) \\
**Voraussetzung:** Zugang zu allen Systemen
Vollständige Bestandsaufnahme aller Zertifikate als Grundlage für die Migration.
----
===== Warum Inventur? =====
| Grund | Beschreibung |
|-------|--------------|
| **Scope** | Wie viele Zertifikate müssen migriert werden? |
| **Algorithmen** | Welche Algorithmen sind im Einsatz? |
| **Ablaufzeiten** | Wann können Zertifikate bei Renewal migriert werden? |
| **Abhängigkeiten** | Welche Systeme hängen zusammen? |
| **Risiken** | Wo sind kritische Zertifikate? |
----
===== Inventur-Quellen =====
flowchart TB
subgraph LOCAL["💻 LOKAL"]
L1[Dateisystem]
L2[Zertifikats-Stores]
L3[Konfigurationsdateien]
end
subgraph NETWORK["🌐 NETZWERK"]
N1[TLS-Endpoints]
N2[LDAP/AD]
N3[HSM]
end
subgraph MGMT["📊 MANAGEMENT"]
M1[CA-Datenbank]
M2[CMDB]
M3[Monitoring]
end
subgraph OUTPUT["📋 OUTPUT"]
O[Inventur-CSV]
end
L1 & L2 & L3 --> O
N1 & N2 & N3 --> O
M1 & M2 & M3 --> O
----
===== Linux: Dateisystem-Scan =====
#!/bin/bash
# cert-inventory-linux.sh
OUTPUT="inventory-linux-$(hostname)-$(date +%Y%m%d).csv"
echo "Pfad,Subject,Issuer,Algorithmus,Schlüssellänge,Gültig_ab,Gültig_bis,Tage_verbleibend,Serial,SANs" > "$OUTPUT"
# Standard-Verzeichnisse
CERT_DIRS=(
"/etc/ssl/certs"
"/etc/pki/tls/certs"
"/etc/nginx/ssl"
"/etc/apache2/ssl"
"/opt/*/ssl"
"/var/lib/docker/volumes/*/ssl"
)
for dir in "${CERT_DIRS[@]}"; do
for cert in $(find $dir -name "*.pem" -o -name "*.crt" -o -name "*.cer" 2>/dev/null); do
# Nur Zertifikate (keine Keys/CSRs)
openssl x509 -in "$cert" -noout 2>/dev/null || continue
subject=$(openssl x509 -in "$cert" -subject -noout | sed 's/subject=//' | tr ',' ';')
issuer=$(openssl x509 -in "$cert" -issuer -noout | sed 's/issuer=//' | tr ',' ';')
algo=$(openssl x509 -in "$cert" -text -noout | grep "Signature Algorithm" | head -1 | awk '{print $3}')
keysize=$(openssl x509 -in "$cert" -text -noout | grep "Public-Key:" | grep -oP '\d+')
not_before=$(openssl x509 -in "$cert" -startdate -noout | cut -d= -f2)
not_after=$(openssl x509 -in "$cert" -enddate -noout | cut -d= -f2)
days_left=$(( ($(date -d "$not_after" +%s) - $(date +%s)) / 86400 ))
serial=$(openssl x509 -in "$cert" -serial -noout | cut -d= -f2)
sans=$(openssl x509 -in "$cert" -text -noout | grep -A1 "Subject Alternative Name" | tail -1 | tr ',' ';')
echo "\"$cert\",\"$subject\",\"$issuer\",\"$algo\",\"$keysize\",\"$not_before\",\"$not_after\",\"$days_left\",\"$serial\",\"$sans\"" >> "$OUTPUT"
done
done
echo "Inventur abgeschlossen: $OUTPUT"
echo "Zertifikate gefunden: $(tail -n +2 "$OUTPUT" | wc -l)"
----
===== Windows: Zertifikats-Store Scan =====
# Cert-Inventory-Windows.ps1
param(
[string]$OutputPath = "inventory-windows-$env:COMPUTERNAME-$(Get-Date -Format 'yyyyMMdd').csv"
)
$results = @()
# Alle Certificate Stores durchsuchen
$stores = @(
@{ Location = "LocalMachine"; Name = "My" },
@{ Location = "LocalMachine"; Name = "Root" },
@{ Location = "LocalMachine"; Name = "CA" },
@{ Location = "LocalMachine"; Name = "WebHosting" },
@{ Location = "CurrentUser"; Name = "My" }
)
foreach ($store in $stores) {
$storePath = "Cert:\$($store.Location)\$($store.Name)"
Get-ChildItem $storePath -ErrorAction SilentlyContinue | ForEach-Object {
$cert = $_
$daysLeft = ($cert.NotAfter - (Get-Date)).Days
# SANs extrahieren
$sanExt = $cert.Extensions | Where-Object { $_.Oid.Value -eq "2.5.29.17" }
$sans = if ($sanExt) { $sanExt.Format($false) } else { "" }
$results += [PSCustomObject]@{
Store = "$($store.Location)\$($store.Name)"
Subject = $cert.Subject
Issuer = $cert.Issuer
Algorithm = $cert.SignatureAlgorithm.FriendlyName
KeySize = $cert.PublicKey.Key.KeySize
NotBefore = $cert.NotBefore
NotAfter = $cert.NotAfter
DaysLeft = $daysLeft
Serial = $cert.SerialNumber
Thumbprint = $cert.Thumbprint
SANs = $sans
HasPrivateKey = $cert.HasPrivateKey
}
}
}
$results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Inventur abgeschlossen: $OutputPath"
Write-Host "Zertifikate gefunden: $($results.Count)"
----
===== Netzwerk: TLS-Endpoint Scan =====
#!/bin/bash
# cert-inventory-network.sh
OUTPUT="inventory-network-$(date +%Y%m%d).csv"
ENDPOINTS_FILE="endpoints.txt"
echo "Endpoint,Subject,Issuer,Algorithmus,Schlüssellänge,Gültig_bis,Tage,Serial" > "$OUTPUT"
# Endpoints aus Datei oder CMDB/DNS
# Format: hostname:port
cat "$ENDPOINTS_FILE" | while read endpoint; do
host=${endpoint%:*}
port=${endpoint#*:}
[ -z "$port" ] && port=443
echo "Scanning $host:$port..."
cert_info=$(echo | openssl s_client -connect "$host:$port" -servername "$host" 2>/dev/null | openssl x509 -text -noout 2>/dev/null)
if [ -n "$cert_info" ]; then
subject=$(echo "$cert_info" | grep "Subject:" | head -1 | sed 's/.*Subject: //' | tr ',' ';')
issuer=$(echo "$cert_info" | grep "Issuer:" | head -1 | sed 's/.*Issuer: //' | tr ',' ';')
algo=$(echo "$cert_info" | grep "Signature Algorithm" | head -1 | awk '{print $3}')
keysize=$(echo "$cert_info" | grep "Public-Key:" | grep -oP '\d+')
not_after=$(echo | openssl s_client -connect "$host:$port" -servername "$host" 2>/dev/null | openssl x509 -enddate -noout | cut -d= -f2)
days_left=$(( ($(date -d "$not_after" +%s) - $(date +%s)) / 86400 ))
serial=$(echo | openssl s_client -connect "$host:$port" -servername "$host" 2>/dev/null | openssl x509 -serial -noout | cut -d= -f2)
echo "\"$endpoint\",\"$subject\",\"$issuer\",\"$algo\",\"$keysize\",\"$not_after\",\"$days_left\",\"$serial\"" >> "$OUTPUT"
else
echo "\"$endpoint\",\"CONNECTION FAILED\",\"\",\"\",\"\",\"\",\"\",\"\"" >> "$OUTPUT"
fi
done
echo "Netzwerk-Inventur abgeschlossen: $OUTPUT"
----
===== CA-Datenbank Inventur =====
#!/bin/bash
# cert-inventory-ca.sh - Aus OpenSSL CA-Index
CA_INDEX="/etc/pki/CA/index.txt"
OUTPUT="inventory-ca-$(date +%Y%m%d).csv"
echo "Status,Serial,Ablauf,Subject,Datei" > "$OUTPUT"
while IFS=$'\t' read -r status expiry revoke serial unknown subject; do
# Status: V=Valid, R=Revoked, E=Expired
status_text=""
case "$status" in
V) status_text="Valid" ;;
R) status_text="Revoked" ;;
E) status_text="Expired" ;;
esac
# Ablaufdatum konvertieren (YYMMDDHHMMSSZ Format)
expiry_formatted=$(date -d "20${expiry:0:2}-${expiry:2:2}-${expiry:4:2}" +%Y-%m-%d 2>/dev/null)
# Zertifikatsdatei
cert_file="/etc/pki/CA/newcerts/${serial}.pem"
echo "\"$status_text\",\"$serial\",\"$expiry_formatted\",\"$subject\",\"$cert_file\"" >> "$OUTPUT"
done < "$CA_INDEX"
echo "CA-Inventur abgeschlossen: $OUTPUT"
echo "Zertifikate:"
echo " Valid: $(grep -c "^\"Valid\"" "$OUTPUT")"
echo " Revoked: $(grep -c "^\"Revoked\"" "$OUTPUT")"
echo " Expired: $(grep -c "^\"Expired\"" "$OUTPUT")"
----
===== Analyse & Reporting =====
#!/usr/bin/env python3
# analyze-inventory.py
import pandas as pd
from datetime import datetime
# Alle Inventur-Dateien laden
df = pd.concat([
pd.read_csv('inventory-linux*.csv'),
pd.read_csv('inventory-windows*.csv'),
pd.read_csv('inventory-network*.csv')
])
# Analyse
print("=== Inventur-Zusammenfassung ===\n")
print(f"Gesamtzahl Zertifikate: {len(df)}")
print("\n--- Nach Algorithmus ---")
print(df['Algorithmus'].value_counts())
print("\n--- Nach Schlüssellänge ---")
print(df['Schlüssellänge'].value_counts())
print("\n--- Ablauf-Analyse ---")
df['Tage_verbleibend'] = pd.to_numeric(df['Tage_verbleibend'], errors='coerce')
print(f"Abgelaufen: {len(df[df['Tage_verbleibend'] < 0])}")
print(f"< 30 Tage: {len(df[(df['Tage_verbleibend'] >= 0) & (df['Tage_verbleibend'] < 30)])}")
print(f"30-90 Tage: {len(df[(df['Tage_verbleibend'] >= 30) & (df['Tage_verbleibend'] < 90)])}")
print(f"90-365 Tage: {len(df[(df['Tage_verbleibend'] >= 90) & (df['Tage_verbleibend'] < 365)])}")
print(f"> 1 Jahr: {len(df[df['Tage_verbleibend'] >= 365])}")
print("\n--- Migration-Potential ---")
rsa = len(df[df['Algorithmus'].str.contains('rsa', case=False, na=False)])
ecdsa = len(df[df['Algorithmus'].str.contains('ecdsa|ec', case=False, na=False)])
hybrid = len(df[df['Algorithmus'].str.contains('ml-dsa|hybrid', case=False, na=False)])
print(f"RSA (zu migrieren): {rsa}")
print(f"ECDSA (zu migrieren): {ecdsa}")
print(f"Hybrid (bereits): {hybrid}")
# Export für Migration-Planung
df.to_excel('inventory-complete.xlsx', index=False)
print("\nVollständige Inventur exportiert: inventory-complete.xlsx")
----
===== Dashboard-Vorlage =====
| Kategorie | Anzahl | Migration |
|-----------|--------|-----------|
| **Algorithmus** | | |
| RSA-2048 | 150 | → Hybrid |
| RSA-4096 | 30 | → Hybrid |
| ECDSA P-256 | 80 | → Hybrid |
| ECDSA P-384 | 20 | → Hybrid |
| ML-DSA/Hybrid | 5 | ✓ Fertig |
| **Ablauf** | | |
| < 30 Tage | 12 | Sofort migrieren |
| 30-90 Tage | 25 | Phase 1 |
| 90-365 Tage | 100 | Phase 2 |
| > 1 Jahr | 148 | Phase 3 |
| **Kritikalität** | | |
| Extern-facing | 40 | Priorität 1 |
| Intern-kritisch | 80 | Priorität 2 |
| Development | 165 | Priorität 3 |
----
===== Checkliste =====
| # | Prüfpunkt | ✓ |
|---|-----------|---|
| 1 | Alle Linux-Server gescannt | ☐ |
| 2 | Alle Windows-Server gescannt | ☐ |
| 3 | Alle TLS-Endpoints gescannt | ☐ |
| 4 | CA-Datenbank exportiert | ☐ |
| 5 | Daten konsolidiert | ☐ |
| 6 | Analyse durchgeführt | ☐ |
| 7 | Migration-Plan erstellt | ☐ |
----
===== Verwandte Dokumentation =====
* [[.:classic-to-hybrid|Classic → Hybrid]] – Migration starten
* [[..:monitoring:ablauf-monitoring|Ablauf-Monitoring]] – Laufende Überwachung
* [[de:int:pqcrypt:business:start|Business]] – Strategische Planung
----
<< [[.:rollback-strategie|← Rollback-Strategie]] | [[..:start|→ Operator-Szenarien]] >>
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//
{{tag>inventur bestandsaufnahme migration analyse operator}}