This commit is contained in:
2025-09-22 20:48:52 +02:00
parent e09bc18900
commit cb639ca052
2 changed files with 60 additions and 12 deletions

63
main.go
View File

@@ -2,6 +2,7 @@ package main
import ( import (
"embed" "embed"
"errors"
"fmt" "fmt"
"html/template" "html/template"
"log" "log"
@@ -43,6 +44,7 @@ type genResult struct {
PrivateArmored string PrivateArmored string
Fingerprint string Fingerprint string
Created time.Time Created time.Time
UIDOnKey string // tatsächliche UID im Schlüssel
} }
func main() { func main() {
@@ -95,27 +97,69 @@ func handleGenerate(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("Fehler beim Erzeugen der Schlüssel: %v", err), http.StatusInternalServerError) http.Error(w, fmt.Sprintf("Fehler beim Erzeugen der Schlüssel: %v", err), http.StatusInternalServerError)
return return
} }
_ = resultTmpl.Execute(w, res) _ = resultTmpl.Execute(w, res)
} }
// sanitizeName entfernt verbotene Zeichen aus Name/Comment-Feldern, die vom OpenPGP-Format für UID reserviert sind
func sanitizeName(s string) string {
s = strings.TrimSpace(s)
// Entferne reservierte und Steuerzeichen, die in der UID nicht vorkommen dürfen
replacer := strings.NewReplacer("<", "", ">", "", "(", "", ")", "", "\r", "", "\n", "")
s = replacer.Replace(s)
// trim doppelte Spaces
for strings.Contains(s, " ") {
s = strings.ReplaceAll(s, " ", " ")
}
return s
}
func sanitizeEmail(s string) string {
s = strings.TrimSpace(s)
s = strings.ReplaceAll(s, " ", "") // keine Spaces
replacer := strings.NewReplacer("<", "", ">", "", "(", "", ")", "", "\r", "", "\n", "")
s = replacer.Replace(s)
return s
}
func validateEmailBasic(s string) bool {
if s == "" {
return false
}
if strings.ContainsAny(s, " <>()\r\n\t") {
return false
}
// sehr einfache Plausibilitätsprüfung
at := strings.IndexByte(s, '@')
if at <= 0 || at == len(s)-1 {
return false
}
if strings.Contains(s[at+1:], "@") {
return false
}
return true
}
func generatePGP(in genInput) (*genResult, error) { func generatePGP(in genInput) (*genResult, error) {
// Kommentar wird in die User-ID integriert // Kommentar nicht in UID einbetten (gopenpgp's GenerateKey nimmt nur Name+Email)
name := in.Name name := sanitizeName(in.Name)
if in.Comment != "" { email := sanitizeEmail(in.Email)
name = fmt.Sprintf("%s (%s)", in.Name, in.Comment) if !validateEmailBasic(email) {
return nil, errors.New("ungültige EMail-Adresse")
}
if name == "" {
return nil, errors.New("Name darf nicht leer sein")
} }
// 1) Schlüssel erzeugen (noch unverschlüsselt) // 1) Schlüssel erzeugen (UID = "Name <email>")
key, err := crypto.GenerateKey(name, in.Email, "rsa", in.RSABits) key, err := crypto.GenerateKey(name, email, "rsa", in.RSABits)
if err != nil { if err != nil {
return nil, fmt.Errorf("GenerateKey: %w", err) return nil, fmt.Errorf("GenerateKey: %w", err)
} }
// 2) Optional mit Passphrase sperren (at-rest Verschlüsselung) // 2) Optional mit Passphrase sperren
if in.Passphrase != "" { if in.Passphrase != "" {
if _, err := key.Lock([]byte(in.Passphrase)); err != nil { if _, err := key.Lock([]byte(in.Passphrase)); err != nil {
return nil, fmt.Errorf("Lock (Passphrase setzen) fehlgeschlagen: %w", err) return nil, fmt.Errorf("Passphrase setzen fehlgeschlagen: %w", err)
} }
} }
@@ -138,6 +182,7 @@ func generatePGP(in genInput) (*genResult, error) {
PrivateArmored: armoredPriv, PrivateArmored: armoredPriv,
Fingerprint: fp, Fingerprint: fp,
Created: created, Created: created,
UIDOnKey: fmt.Sprintf("%s <%s>", name, email),
}, nil }, nil
} }

View File

@@ -23,7 +23,10 @@
<div class="card"> <div class="card">
<div><strong>Fingerprint:</strong> {{.Fingerprint}}</div> <div><strong>Fingerprint:</strong> {{.Fingerprint}}</div>
<div><strong>Erstellt:</strong> {{.Created.Format "2006-01-02 15:04:05"}}</div> <div><strong>Erstellt:</strong> {{.Created.Format "2006-01-02 15:04:05"}}</div>
<div class="muted">User ID: {{.Name}} {{if .Comment}}({{.Comment}}){{end}} &lt;{{.Email}}&gt; • RSA {{.RSABits}}</div> <div class="muted">User ID im Schlüssel: {{if .UIDOnKey}}{{.UIDOnKey}}{{else}}{{.Name}} &lt;{{.Email}}&gt;{{end}} • RSA {{.RSABits}}</div>
{{if .Comment}}
<div class="muted">Kommentar (nur Anzeige, nicht in UID eingebettet): {{.Comment}}</div>
{{end}}
</div> </div>
<div class="card"> <div class="card">
@@ -42,7 +45,7 @@
<button onclick="copy('#priv')">Kopieren</button> <button onclick="copy('#priv')">Kopieren</button>
<button onclick="download('#priv','private.asc')">Als Datei speichern</button> <button onclick="download('#priv','private.asc')">Als Datei speichern</button>
</div> </div>
<p class="muted">Verwahren Sie den privaten Schlüssel sicher. Teilen Sie ihn niemals. Nutzen Sie eine starke Passphrase.</p> <p class="muted">Verwahren Sie den privaten Schlüssel sicher. Teilen Sie ihn niemals.</p>
</div> </div>
<script> <script>
@@ -62,4 +65,4 @@
} }
</script> </script>
</body> </body>
</html> </html>