Verschlüsselung, Signierung und Zertifikatsprüfung direkt in Bash
Wer Backups, Konfigurationsdateien und API-Secrets in der Shell verwaltet, kommt an OpenSSL und GPG nicht vorbei. Dieser Artikel zeigt, wie man beide Werkzeuge sicher und praxisnah in Bash-Skripten einsetzt — von symmetrischer Verschlüsselung über GPG-Signierung bis zur automatisierten Zertifikatsprüfung ohne interaktive Passwort-Eingabe.
Inhaltsverzeichnis
- 1. Warum OpenSSL und GPG in Shell-Skripten gehören
- 2. Symmetrische Verschlüsselung mit OpenSSL
- 3. Asymmetrische Verschlüsselung mit OpenSSL-RSA
- 4. GPG-Grundlagen: Schlüsselring und Keyserver in Skripten
- 5. Dateien signieren und Signaturen prüfen mit GPG
- 6. Zertifikatsprüfung und Ablaufdaten mit OpenSSL überwachen
- 7. Sicheres Passwort-Handling ohne Klartext in Prozessliste
- 8. Secrets in Shell-Skripten: Umgebungsvariablen vs. Dateien
- 9. OpenSSL vs. GPG im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum OpenSSL und GPG in Shell-Skripten gehören
Backup-Skripte, die Daten unverschlüsselt in einen S3-Bucket schieben, Deployment-Skripte, die Passwörter als Klartext in Umgebungsvariablen weitergeben, Monitoring-Skripte, die abgelaufene TLS-Zertifikate erst beim Ausfall bemerken — das sind reale Probleme in produktiven Umgebungen, die sich mit wenigen Zeilen OpenSSL und GPG in Shell-Skripten lösen lassen. Die beiden Werkzeuge sind auf nahezu jedem Linux-Server vorinstalliert, haben stabile CLIs und lassen sich vollständig ohne interaktive Eingabe betreiben. Genau das macht sie zur ersten Wahl für Shell-Automatisierung im Sicherheitsbereich.
Der Unterschied zwischen den beiden liegt im Einsatzszenario: OpenSSL in Shell-Skripten ist die richtige Wahl für TLS-Zertifikate, symmetrische Dateiverschlüsselung mit AES und schnelle kryptografische Hash-Operationen. GPG in Shell-Skripten übernimmt asymmetrische Verschlüsselung mit öffentlichen Schlüsseln, digitale Signaturen für Artefakte und Release-Bundles sowie die Verwaltung eines Schlüsselrings für mehrere Empfänger. Wer beide Tools kennt und situationsgerecht einsetzt, deckt das komplette Spektrum an kryptografischen Shell-Operationen ab — von einfacher Passwortverschlüsselung bis zum signierten Paket-Release.
Ein wichtiger Grundsatz, bevor die ersten Codebeispiele kommen: Passwörter und Schlüsselmaterial dürfen niemals als Kommandozeilen-Argument übergeben werden. Die Prozessliste (ps aux) ist auf mehrbenutzer Systemen für alle Benutzer lesbar. Beide Tools bieten Alternativen — Datei-basierte Passwort-Übergabe, Umgebungsvariablen über spezielle Optionen oder direkte Pipe-Übergabe. Diese Techniken sind in jedem der folgenden Abschnitte konsequent angewendet.
2. Symmetrische Verschlüsselung mit OpenSSL
Die symmetrische Verschlüsselung mit OpenSSL in Shell-Skripten nutzt typischerweise AES-256-GCM oder AES-256-CBC. AES-256-GCM ist die modernere Wahl: Sie bietet sowohl Vertraulichkeit als auch Integrität (Authenticated Encryption). Für Backup-Skripte und die Verschlüsselung von Konfigurationsdateien ist das der empfohlene Algorithmus. Der Befehl openssl enc -aes-256-gcm übernimmt Passwort-Ableitung, Salting und die eigentliche Verschlüsselung in einem Schritt. Mit der Option -pass file:/dev/stdin oder -pass env:VARNAME bleibt das Passwort aus der Prozessliste heraus.
Für die Schlüsselableitung aus einem Passwort sollte man immer -pbkdf2 -iter 600000 angeben. Ohne diese Option verwendet OpenSSL die veraltete EVP_BytesToKey-Funktion, die deutlich schwächer gegen Brute-Force-Angriffe ist. PBKDF2 mit 600.000 Iterationen entspricht den aktuellen NIST-Empfehlungen für passwortbasierte Schlüsselableitung. Wer den generierten Schlüssel für mehrere Operationen wiederverwenden will, leitet ihn einmalig mit openssl kdf ab und speichert ihn sicher — das vermeidet wiederholte Ableitung in Schleifen.
#!/usr/bin/env bash
# backup-encrypt.sh — Encrypt backups with AES-256-GCM, no password in process list
set -euo pipefail
BACKUP_DIR="/var/backup"
ENCRYPTED_DIR="/var/backup/encrypted"
PASS_FILE="/etc/backup/.backup_passphrase" # mode 600, owned by root
encrypt_file() {
local src="$1"
local dest="${ENCRYPTED_DIR}/$(basename "$src").enc"
# -pass file: keeps password out of process list; -pbkdf2 uses modern KDF
openssl enc -aes-256-gcm \
-in "$src" \
-out "$dest" \
-pass "file:${PASS_FILE}" \
-pbkdf2 -iter 600000 \
-salt
echo "[OK] Encrypted: $(basename "$src")"
}
decrypt_file() {
local src="$1"
local dest="${BACKUP_DIR}/$(basename "${src%.enc"}")"
openssl enc -aes-256-gcm -d \
-in "$src" \
-out "$dest" \
-pass "file:${PASS_FILE}" \
-pbkdf2 -iter 600000
echo "[OK] Decrypted: $(basename "$src")"
}
mkdir -p "$ENCRYPTED_DIR"
# Encrypt all .sql.gz files created in the last 24 hours
while IFS= read -r -d '' f; do
encrypt_file "$f"
done < <(find "$BACKUP_DIR" -maxdepth 1 -name "*.sql.gz" -mtime -1 -print0)
3. Asymmetrische Verschlüsselung mit OpenSSL-RSA
Für Szenarien, in denen mehrere Parteien Daten verschlüsseln sollen, die nur eine einzige Partei entschlüsseln kann, ist asymmetrische Verschlüsselung mit RSA oder Elliptic-Curve-Kryptografie der richtige Ansatz. OpenSSL in Shell-Skripten bietet dafür openssl pkeyutl — das modernere Interface gegenüber dem älteren openssl rsautl. RSA kann direkt nur kleine Datenmengen verschlüsseln. Das Standardmuster in Shell-Skripten ist deshalb Hybrid-Verschlüsselung: einen zufälligen symmetrischen Schlüssel mit dem öffentlichen RSA-Schlüssel verschlüsseln und die eigentlichen Daten mit diesem Session-Key mit AES verschlüsseln.
Schlüsselgenerierung für den Produktionseinsatz: openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 erzeugt einen RSA-4096-Schlüssel. Für neue Projekte sind Elliptic-Curve-Schlüssel (P-256 oder X25519) die bessere Wahl — kleinere Schlüsselgröße, gleichwertige oder höhere Sicherheit, schnellere Operationen. Wichtig: Private Schlüssel erhalten Dateiberechtigungen 600, werden nie in ein Repository eingecheckt und sollten für nicht-interaktiven Einsatz mit einer Passphrase geschützt sein, die über einen Secret-Manager bereitgestellt wird.
#!/usr/bin/env bash
# hybrid-encrypt.sh — Hybrid encryption: RSA key wrap + AES-256-GCM data encryption
set -euo pipefail
PUBLIC_KEY="/etc/deploy/recipient.pub.pem"
PRIVATE_KEY="/etc/deploy/recipient.priv.pem"
hybrid_encrypt() {
local plaintext="$1"
local outdir="$2"
# Generate a random 32-byte session key
local session_key
session_key="$(openssl rand -hex 32)"
# Encrypt session key with recipient's public RSA key
printf '%s' "$session_key" | \
openssl pkeyutl -encrypt -pubin -inkey "$PUBLIC_KEY" \
-pkeyopt rsa_padding_mode:oaep \
-pkeyopt rsa_oaep_md:sha256 \
> "${outdir}/session.key.enc"
# Encrypt the actual data with the session key (AES-256-GCM)
openssl enc -aes-256-gcm \
-in "$plaintext" \
-out "${outdir}/data.enc" \
-pass "pass:${session_key}" \
-pbkdf2 -iter 1
# Unset session key from memory
unset session_key
echo "[OK] Hybrid-encrypted to ${outdir}/"
}
hybrid_decrypt() {
local indir="$1"
local outfile="$2"
# Recover session key using private key
local session_key
session_key="$(openssl pkeyutl -decrypt -inkey "$PRIVATE_KEY" \
-pkeyopt rsa_padding_mode:oaep \
-pkeyopt rsa_oaep_md:sha256 \
< "${indir}/session.key.enc")"
openssl enc -aes-256-gcm -d \
-in "${indir}/data.enc" \
-out "$outfile" \
-pass "pass:${session_key}" \
-pbkdf2 -iter 1
unset session_key
echo "[OK] Decrypted to ${outfile}"
}
4. GPG-Grundlagen: Schlüsselring und Keyserver in Skripten
GPG in nicht-interaktiven Shell-Skripten erfordert einige spezifische Konfigurationsschritte, die in interaktiver Nutzung nicht nötig sind. Das wichtigste Flag für Shell-Skripte ist --batch kombiniert mit --no-tty — ohne diese Optionen versucht GPG, mit dem Terminal zu interagieren, was in cron-Jobs und CI-Pipelines zu Hängen führt. Für automatisierte Signiervorgänge, die keinen Benutzer am Terminal haben, muss der Schlüssel im Agent ohne Passphrase vorliegen oder der GPG-Agent muss so konfiguriert sein, dass er die Passphrase über eine Pinentry-Datei bezieht.
Ein eigenes GPG-Homeverzeichnis (--homedir /etc/deploy/gnupg) mit Berechtigungen 700 für Skripte zu verwenden, die als Systemdienst laufen, ist eine wichtige Sicherheitsmaßnahme. So werden die Schlüssel nicht im Home-Verzeichnis eines regulären Benutzers gespeichert und können fein granular in der Zugriffskontrolle des Betriebssystems abgesichert werden. Beim Import öffentlicher Schlüssel für Empfänger empfiehlt sich --import-options import-minimal, um keine unnötigen Signaturen und Benutzerdaten zu importieren, die Verifikationen verlangsamen.
5. Dateien signieren und Signaturen prüfen mit GPG
Die digitale Signierung von Release-Artefakten mit GPG in Shell-Skripten ist ein Kernbestandteil sicherer Release-Prozesse. Eine abgetrennte Signatur (gpg --detach-sign) ist der Datei-gebundenen Signatur vorzuziehen, weil sie das Original unverändert lässt und Verifikation auch ohne GPG-Installation möglich ist, solange der öffentliche Schlüssel bekannt ist. Die Signatur-Datei erhält die Endung .sig und wird zusammen mit dem Artefakt verteilt. Für maschinelle Verifikation liefert gpg --verify --status-fd 2 strukturierten Output auf stderr, den ein Shell-Skript zuverlässig parsen kann.
Verschlüsselung für mehrere Empfänger — ein häufiges Szenario in Teams — ist mit GPG einfacher als mit reiner RSA-Implementierung. gpg --encrypt --recipient alice@example.com --recipient bob@example.com erzeugt ein Paket, das beide Empfänger mit ihrem eigenen Schlüssel entschlüsseln können. Das zugrundeliegende Mechanismus ist ebenfalls Hybrid-Verschlüsselung: GPG generiert intern einen Session-Key, verschlüsselt ihn für jeden Empfänger separat und verschlüsselt die Daten einmal mit dem Session-Key. Das kombinierte Signieren und Verschlüsseln (--sign --encrypt) in einem Schritt ist für Szenarien empfehlenswert, in denen sowohl Vertraulichkeit als auch Authentizität gefordert sind.
#!/usr/bin/env bash
# gpg-release.sh — Sign release artifacts and verify signatures
set -euo pipefail
GPG_HOMEDIR="/etc/deploy/gnupg"
SIGNING_KEY_ID="0xABCD1234EFGH5678" # Use key fingerprint in production
RELEASE_DIR="/var/releases"
sign_artifact() {
local file="$1"
# --batch and --no-tty prevent interactive prompts in CI
gpg --homedir "$GPG_HOMEDIR" \
--batch --no-tty \
--local-user "$SIGNING_KEY_ID" \
--detach-sign \
--armor \
--output "${file}.sig" \
"$file"
echo "[OK] Signed: $(basename "$file") → $(basename "${file}.sig")"
}
verify_artifact() {
local file="$1"
local sigfile="${file}.sig"
[[ -f "$sigfile" ]] || { echo "[ERROR] Signature file missing: $sigfile" >&2; return 1; }
# --status-fd 2 writes machine-readable status to stderr
local gpg_status
gpg_status="$(gpg --homedir "$GPG_HOMEDIR" \
--batch --no-tty \
--status-fd 1 \
--verify "$sigfile" "$file" 2>/dev/null)"
if echo "$gpg_status" | grep -q "GOODSIG"; then
echo "[OK] Signature valid: $(basename "$file")"
return 0
else
echo "[ERROR] Signature INVALID: $(basename "$file")" >&2
return 1
fi
}
# Sign all .tar.gz files in release directory
while IFS= read -r -d '' f; do
sign_artifact "$f"
done < <(find "$RELEASE_DIR" -maxdepth 1 -name "*.tar.gz" -print0)
# Verify all signatures
while IFS= read -r -d '' f; do
verify_artifact "$f"
done < <(find "$RELEASE_DIR" -maxdepth 1 -name "*.tar.gz" -print0)
6. Zertifikatsprüfung und Ablaufdaten mit OpenSSL überwachen
TLS-Zertifikate, die unbemerkt ablaufen, gehören zu den häufigsten Ursachen für ungeplante Ausfälle. Mit OpenSSL in Shell-Skripten lassen sich Ablaufdaten automatisch prüfen und Alarme früh genug auslösen. Der Befehl openssl x509 -noout -enddate gibt das Ablaufdatum eines Zertifikats aus; mit openssl s_client lässt sich das Zertifikat eines laufenden HTTPS-Servers direkt abrufen, ohne eine Datei herunterladen zu müssen. Für Skripte, die regelmäßig via cron laufen, ist die Kombination beider Befehle ideal: Zertifikat abrufen, Ablaufdatum parsen, mit einem Schwellenwert vergleichen und bei Bedarf einen Alert senden.
Das Parsen des Ablaufdatums erfordert einen Umweg über date, weil OpenSSL das Datum im Format notAfter=May 9 12:00:00 2027 GMT ausgibt. Mit date -d "$(openssl x509 -noout -enddate -in cert.pem | cut -d= -f2)" +%s erhält man den Unix-Timestamp und kann ihn mit dem aktuellen Zeitstempel vergleichen. Wichtig: Auf macOS erwartet date ein anderes Format als auf Linux. Für portable Skripte ist entweder Plattform-Detection oder die Verwendung von Python für die Datumsumrechnung die pragmatische Lösung — oder man nutzt konsequent openssl x509 -checkend N, das direkt prüft, ob ein Zertifikat in den nächsten N Sekunden abläuft, ohne Datums-Parsing.
#!/usr/bin/env bash
# cert-monitor.sh — Check TLS certificate expiry for a list of domains
set -euo pipefail
WARN_DAYS=30
CRITICAL_DAYS=7
DOMAINS=(
"mironsoft.de"
"api.mironsoft.de"
"shop.mironsoft.de"
)
check_cert_expiry() {
local domain="$1"
local port="${2:-443}"
# Retrieve certificate from live server; timeout after 5s
local cert
cert="$(echo | timeout 5 openssl s_client \
-servername "$domain" \
-connect "${domain}:${port}" \
2>/dev/null | openssl x509 2>/dev/null)"
[[ -z "$cert" ]] && { echo "[ERROR] Could not retrieve cert for ${domain}" >&2; return 1; }
# openssl -checkend N returns 0 if cert is valid for N more seconds
local warn_secs=$(( WARN_DAYS * 86400 ))
local crit_secs=$(( CRITICAL_DAYS * 86400 ))
if ! echo "$cert" | openssl x509 -noout -checkend "$crit_secs"; then
echo "[CRITICAL] ${domain}: certificate expires in less than ${CRITICAL_DAYS} days!"
return 2
elif ! echo "$cert" | openssl x509 -noout -checkend "$warn_secs"; then
local enddate
enddate="$(echo "$cert" | openssl x509 -noout -enddate | cut -d= -f2)"
echo "[WARN] ${domain}: certificate expires on ${enddate}"
return 1
else
local enddate
enddate="$(echo "$cert" | openssl x509 -noout -enddate | cut -d= -f2)"
echo "[OK] ${domain}: valid until ${enddate}"
return 0
fi
}
exit_code=0
for domain in "${DOMAINS[@]}"; do
check_cert_expiry "$domain" || exit_code=$?
done
exit $exit_code
7. Sicheres Passwort-Handling ohne Klartext in Prozessliste
Das größte Sicherheitsproblem beim Einsatz von OpenSSL und GPG in Shell-Skripten ist die unachtsame Übergabe von Passwörtern als Kommandozeilen-Argument. Ein Aufruf wie openssl enc -aes-256-cbc -pass pass:meinpasswort macht das Passwort für alle Benutzer auf dem System über ps aux sichtbar — auch für kurze Zeit, solange der Prozess läuft. Das ist in Mehrbenutzersystemen und vor allem in Kubernetes-Pods oder Shared-CI-Umgebungen ein reales Risiko. Die korrekte Methode ist immer entweder -pass file:/path/to/passfile, -pass env:VARNAME oder die Übergabe über stdin mit -pass fd:0.
Für GPG ist das analoge Muster --passphrase-file /path/to/passfile oder --passphrase-fd 0. Passwort-Dateien müssen Berechtigungen 600 haben, nur root oder dem Dienstaccount gehören und idealerweise auf einem tmpfs-Dateisystem liegen, das nicht auf die Festplatte geschrieben wird. Ein alternatives Muster für kurzzeitig benötigte Passwörter ist die Nutzung einer named pipe: mkfifo /tmp/passfifo; echo "passphrase" > /tmp/passfifo & gpg --passphrase-fd 3 3 — so wird das Passwort nie in einer Datei gespeichert. Für die sichersten Umgebungen ist ein Secret-Manager wie HashiCorp Vault oder AWS Secrets Manager die richtige Wahl, mit Shell-Integration über die jeweiligen CLIs.
8. Secrets in Shell-Skripten: Umgebungsvariablen vs. Dateien
Die Frage, ob Secrets als Umgebungsvariablen oder als Dateien übergeben werden sollen, hat in Shell-Skripten keine einfache Antwort. Umgebungsvariablen sind in /proc/PID/environ lesbar — auf Linux nur für den Eigentümer des Prozesses und root, aber in einigen Container-Umgebungen kann das Exposure breiter sein. Dateien mit Berechtigungen 600 sind durch das Unix-Dateisystem abgesichert, aber hinterlassen persistente Spuren im Dateisystem. Die pragmatische Lösung in modernen Deployments: Secrets aus einem Secret-Manager holen, kurzzeitig in einer Umgebungsvariable halten, für kryptografische Operationen in eine temporäre Datei schreiben (auf tmpfs), danach sofort löschen.
Das Löschen sensitiver Variablen aus der Shell-Umgebung geschieht mit unset VARIABLE_NAME. Das verhindert nicht, dass child processes die Variable erben, wenn sie bereits exportiert war — deshalb sollte man sensitive Variablen möglichst spät exportieren und so früh wie möglich wieder löschen. Für OpenSSL und GPG in Shell-Skripten, die in Docker-Containern oder Kubernetes-Pods laufen, ist die empfohlene Architektur: Secret via Kubernetes Secret oder Vault Agent als Datei in ein tmpfs-Volume mounten, in der kryptografischen Operation direkt auf die Datei referenzieren, und sicherstellen, dass das tmpfs-Volume nicht in Image-Layers oder Container-Snapshots landet.
9. OpenSSL vs. GPG im direkten Vergleich
Beide Tools überschneiden sich in einigen Bereichen, haben aber klar unterschiedliche Stärken. Die Wahl zwischen OpenSSL und GPG in Shell-Skripten hängt vom konkreten Anwendungsfall ab — die folgende Tabelle zeigt die wichtigsten Unterschiede.
| Kriterium | OpenSSL | GPG | Empfehlung |
|---|---|---|---|
| Sym. Dateiverschlüsselung | AES-256-GCM, PBKDF2 | AES-256, Passphrase | OpenSSL für Skripte |
| Mehrere Empfänger | Manuell Hybrid-Impl. | --recipient nativ | GPG |
| Digitale Signatur | openssl dgst -sign | --detach-sign, Web of Trust | GPG für Releases |
| TLS-Zertifikate prüfen | openssl s_client, x509 | Nicht vorgesehen | OpenSSL |
| Non-interaktiver Betrieb | Native, kein Agent nötig | --batch --no-tty nötig | OpenSSL einfacher |
| Schlüsselring / Team | Manuell verwalten | Keyserver, Web of Trust | GPG |
In der Praxis ist es keine Entweder-oder-Entscheidung: Deployment-Skripte können OpenSSL für die Backup-Verschlüsselung und TLS-Prüfung verwenden und gleichzeitig GPG für die Verifikation heruntergeladener Pakete und die Signierung von Release-Artefakten einsetzen. Wichtig ist, dass die gewählte Methode konsequent und ohne Abkürzungen beim Passwort-Handling eingesetzt wird.
Mironsoft
Shell-Sicherheit, Kryptografie-Integration und Secrets-Management
Kryptografie in euren Shell-Skripten korrekt umgesetzt?
Wir prüfen bestehende Backup- und Deployment-Skripte auf unsichere Passwort-Übergabe, schwache Algorithmen und fehlende Zertifikatsprüfung — und ersetzen fragile Lösungen durch robuste OpenSSL- und GPG-Integration.
Security-Audit
Shell-Skripte auf unsichere Passwort-Übergabe und schwache Algorithmen prüfen
Backup-Verschlüsselung
AES-256-GCM-Backup-Pipelines mit sicherem Schlüssel-Management aufbauen
Zertifikats-Monitoring
Automatisiertes TLS-Monitoring mit OpenSSL und Alert-Integration einrichten
10. Zusammenfassung
Der praktische Einsatz von OpenSSL und GPG in Shell-Skripten erfordert konsequente Anwendung einiger Grundregeln: Passwörter nie als Kommandozeilen-Argument übergeben, immer --batch und --no-tty für nicht-interaktiven GPG-Betrieb setzen, AES-256-GCM mit PBKDF2 für symmetrische Verschlüsselung verwenden und openssl x509 -checkend für portable Zertifikatsprüfung einsetzen. Hybrid-Verschlüsselung verbindet die Stärken beider Welten: RSA oder GPG-Keys für die Schlüsselverteilung, AES für die eigentlichen Datenmassen.
Die wichtigste Erkenntnis für Teams: Kryptografie-Code in Shell-Skripten ist kein Einmal-Setup, sondern muss regelmäßig überprüft werden. Algorithmen veralten, Schlüssel laufen ab, neue Angriffsvektoren entstehen. Das konsequente Monitoring von Zertifikatsablauf und regelmäßige Rotation von Verschlüsselungsschlüsseln sind genauso Teil einer sicheren Shell-Automatisierung wie die korrekte initiale Implementierung.
OpenSSL und GPG in Shell-Skripten — Das Wichtigste auf einen Blick
Passwort-Sicherheit
Niemals -pass pass:wort — immer -pass file:, -pass env: oder -pass fd:0. Passwörter in Prozessliste sind auf Mehrbenutzersystemen lesbar.
Algorithmen-Wahl
AES-256-GCM mit PBKDF2 (600k Iterationen) für symmetrische Verschlüsselung. RSA-OAEP-SHA256 oder X25519 für asymmetrisch. Veraltete DES/3DES vermeiden.
GPG non-interaktiv
--batch --no-tty für CI und cron. Eigenes --homedir mit Modus 700 für Systemdienste. --status-fd 1 für maschinenlesbaren Output.
Zertifikats-Monitoring
openssl x509 -checkend N für portable Ablaufprüfung. openssl s_client für Live-Server. Alarm 30 Tage vor Ablauf senden.