RC1
This commit is contained in:
63
main.go
63
main.go
@@ -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 E‑Mail-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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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}} <{{.Email}}> • RSA {{.RSABits}}</div>
|
<div class="muted">User ID im Schlüssel: {{if .UIDOnKey}}{{.UIDOnKey}}{{else}}{{.Name}} <{{.Email}}>{{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>
|
||||||
Reference in New Issue
Block a user