run1
This commit is contained in:
152
main.go
Normal file
152
main.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||
)
|
||||
|
||||
//go:embed templates/*
|
||||
var templateFS embed.FS
|
||||
|
||||
var (
|
||||
indexTmpl = mustParse("templates/index.html")
|
||||
resultTmpl = mustParse("templates/result.html")
|
||||
)
|
||||
|
||||
func mustParse(path string) *template.Template {
|
||||
t, err := template.ParseFS(templateFS, path)
|
||||
if err != nil {
|
||||
log.Fatalf("template parse error for %s: %v", path, err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
type genInput struct {
|
||||
Name string
|
||||
Email string
|
||||
Comment string
|
||||
RSABits int
|
||||
Passphrase string
|
||||
}
|
||||
|
||||
type genResult struct {
|
||||
genInput
|
||||
PublicArmored string
|
||||
PrivateArmored string
|
||||
Fingerprint string
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", handleIndex)
|
||||
mux.HandleFunc("/generate", handleGenerate)
|
||||
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200); _, _ = w.Write([]byte("ok")) })
|
||||
|
||||
addr := ":8081"
|
||||
log.Printf("PGP Keygen läuft auf http://localhost%s", addr)
|
||||
if err := http.ListenAndServe(addr, securityHeaders(mux)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
_ = indexTmpl.Execute(w, map[string]any{})
|
||||
}
|
||||
|
||||
func handleGenerate(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "Ungültige Eingaben", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
in := genInput{
|
||||
Name: strings.TrimSpace(r.FormValue("name")),
|
||||
Email: strings.TrimSpace(r.FormValue("email")),
|
||||
Comment: strings.TrimSpace(r.FormValue("comment")),
|
||||
Passphrase: r.FormValue("passphrase"),
|
||||
}
|
||||
|
||||
bitsStr := r.FormValue("rsabits")
|
||||
if bitsStr == "" {
|
||||
bitsStr = "4096"
|
||||
}
|
||||
bits, err := strconv.Atoi(bitsStr)
|
||||
if err != nil || (bits != 2048 && bits != 3072 && bits != 4096) {
|
||||
bits = 4096
|
||||
}
|
||||
in.RSABits = bits
|
||||
|
||||
res, err := generatePGP(in)
|
||||
if err != nil {
|
||||
log.Printf("generate error: %v", err)
|
||||
http.Error(w, fmt.Sprintf("Fehler beim Erzeugen der Schlüssel: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_ = resultTmpl.Execute(w, res)
|
||||
}
|
||||
|
||||
func generatePGP(in genInput) (*genResult, error) {
|
||||
// Kommentar wird in die User-ID integriert
|
||||
name := in.Name
|
||||
if in.Comment != "" {
|
||||
name = fmt.Sprintf("%s (%s)", in.Name, in.Comment)
|
||||
}
|
||||
|
||||
// 1) Schlüssel erzeugen (noch unverschlüsselt)
|
||||
key, err := crypto.GenerateKey(name, in.Email, "rsa", in.RSABits)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GenerateKey: %w", err)
|
||||
}
|
||||
|
||||
// 2) Optional mit Passphrase sperren (at-rest Verschlüsselung)
|
||||
if in.Passphrase != "" {
|
||||
if _, err := key.Lock([]byte(in.Passphrase)); err != nil {
|
||||
return nil, fmt.Errorf("Lock (Passphrase setzen) fehlgeschlagen: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Armored ausgeben
|
||||
armoredPriv, err := key.Armor()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("armor private: %w", err)
|
||||
}
|
||||
armoredPub, err := key.GetArmoredPublicKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("armor public: %w", err)
|
||||
}
|
||||
|
||||
fp := strings.ToUpper(key.GetFingerprint())
|
||||
created := time.Now()
|
||||
|
||||
return &genResult{
|
||||
genInput: in,
|
||||
PublicArmored: armoredPub,
|
||||
PrivateArmored: armoredPriv,
|
||||
Fingerprint: fp,
|
||||
Created: created,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// --- security headers middleware ---
|
||||
func securityHeaders(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'")
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.Header().Set("Referrer-Policy", "no-referrer")
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user