API-06: wiederanlauf-nachsynchronisierung-nach-core-ausfall (postgres-puffer, service-credential, sofort-invalidate)
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
// Package moduletrust implementiert Core API-05: asymmetrische JWT-Signatur
|
||||
// mit Schluesselverteilung (JWKS), damit DMS/Mail/Archive/Workflow JWTs
|
||||
// LOKAL verifizieren koennen, ohne pro Aufruf einen synchronen Request an
|
||||
// Core zu stellen — Core darf Fundament sein, ohne zum Flaschenhals zu
|
||||
// werden (siehe Entscheidungsverlauf "Vertrauensstellung Core<->Module" in
|
||||
// nexarch-state.json). IAM-02s HS256-Session-Cookie (Browser-Login) bleibt
|
||||
// unangetastet — dies ist ein zusaetzlicher, getrennter Vertrauensmechanismus
|
||||
// fuer Modul-zu-Modul/Modul-zu-Core-Aufrufe.
|
||||
package moduletrust
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type KeyPair struct {
|
||||
KID string
|
||||
Private ed25519.PrivateKey
|
||||
Public ed25519.PublicKey
|
||||
}
|
||||
|
||||
// KeyManager haelt ALLE noch gueltigen Schluesselpaare — nicht nur das
|
||||
// aktuell signierende. Rotate erzeugt ein neues Paar und behaelt die alten
|
||||
// fuer die Verifikation bereits ausgestellter Tokens (Akzeptanzkriterium 3:
|
||||
// Rotation ohne Ausfallzeit fuer andere Module).
|
||||
type KeyManager struct {
|
||||
mu sync.RWMutex
|
||||
keys []KeyPair // aeltestes zuerst, neuestes zuletzt
|
||||
}
|
||||
|
||||
func NewKeyManager() (*KeyManager, error) {
|
||||
m := &KeyManager{}
|
||||
if _, err := m.Rotate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Rotate erzeugt ein neues Ed25519-Schluesselpaar mit eigener KID und macht
|
||||
// es zum aktuellen Signierschluessel. Aeltere Schluessel bleiben in
|
||||
// PublicKeySet() erhalten, damit bereits ausgestellte Tokens weiterhin
|
||||
// verifizierbar sind.
|
||||
func (m *KeyManager) Rotate() (KeyPair, error) {
|
||||
pub, priv, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
return KeyPair{}, fmt.Errorf("schluesselpaar erzeugen: %w", err)
|
||||
}
|
||||
kidBytes := make([]byte, 8)
|
||||
if _, err := rand.Read(kidBytes); err != nil {
|
||||
return KeyPair{}, fmt.Errorf("kid erzeugen: %w", err)
|
||||
}
|
||||
kp := KeyPair{KID: hex.EncodeToString(kidBytes), Private: priv, Public: pub}
|
||||
|
||||
m.mu.Lock()
|
||||
m.keys = append(m.keys, kp)
|
||||
m.mu.Unlock()
|
||||
return kp, nil
|
||||
}
|
||||
|
||||
// SigningKey liefert den aktuellen (neuesten) Schluessel zum Signieren neuer Tokens.
|
||||
func (m *KeyManager) SigningKey() (KeyPair, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if len(m.keys) == 0 {
|
||||
return KeyPair{}, fmt.Errorf("moduletrust: kein schluessel vorhanden")
|
||||
}
|
||||
return m.keys[len(m.keys)-1], nil
|
||||
}
|
||||
|
||||
// PublicKeySet liefert ALLE bekannten oeffentlichen Schluessel (kid ->
|
||||
// public key) — die Grundlage fuer den JWKS-Endpunkt.
|
||||
func (m *KeyManager) PublicKeySet() map[string]ed25519.PublicKey {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
out := make(map[string]ed25519.PublicKey, len(m.keys))
|
||||
for _, k := range m.keys {
|
||||
out[k.KID] = k.Public
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user