WACS-Kompatibilität
All checks were successful
release-tag / release-image (push) Successful in 1m47s

This commit is contained in:
jbergner
2025-04-29 14:15:37 +02:00
parent 47e89b46bc
commit 1765d55ac6

55
main.go
View File

@@ -420,45 +420,55 @@ type jwsPayload struct {
JWK *jose.JSONWebKey JWK *jose.JSONWebKey
} }
// -----------------------------------------------------------------------------
// JWS verification helper supports both JSON & compact JWS formats
// -----------------------------------------------------------------------------
func (s *server) verifyJWS(ctx context.Context, w http.ResponseWriter, r *http.Request) (*jwsPayload, bool) { func (s *server) verifyJWS(ctx context.Context, w http.ResponseWriter, r *http.Request) (*jwsPayload, bool) {
if !jwsVerify { if !jwsVerify {
data, _ := io.ReadAll(r.Body) data, _ := io.ReadAll(r.Body)
return &jwsPayload{Data: data}, true return &jwsPayload{Data: data}, true
} }
// Decode incoming JWS (JSON serialization expected) // read full body once
raw, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "read body", http.StatusBadRequest)
return nil, false
}
// try JWS JSON first …
var sig jose.JSONWebSignature var sig jose.JSONWebSignature
if err := json.NewDecoder(r.Body).Decode(&sig); err != nil { if err := json.Unmarshal(raw, &sig); err != nil || len(sig.Signatures) == 0 {
http.Error(w, "bad JWS", http.StatusBadRequest) // … fallback to compact serialization
return nil, false cp, err2 := jose.ParseSigned(string(raw))
} if err2 != nil {
if len(sig.Signatures) == 0 { http.Error(w, "bad JWS", http.StatusBadRequest)
http.Error(w, "no signature", http.StatusBadRequest) return nil, false
return nil, false }
sig = *cp
} }
prot := sig.Signatures[0].Protected // already decoded in gojose v3 prot := sig.Signatures[0].Protected // already base-64 decoded
// Nonce replayprotection // Nonce replay-protection
if !s.db.takeNonce(ctx, prot.Nonce) { if !s.db.takeNonce(ctx, prot.Nonce) {
http.Error(w, "bad nonce", http.StatusForbidden) http.Error(w, "bad nonce", http.StatusForbidden)
return nil, false return nil, false
} }
// Resolve verification key (inline JWK or referenced by kid) // Resolve verification key
var key *jose.JSONWebKey var key *jose.JSONWebKey
if prot.JSONWebKey != nil { switch {
case prot.JSONWebKey != nil:
key = prot.JSONWebKey key = prot.JSONWebKey
} else if prot.KeyID != "" { case prot.KeyID != "":
id := path.Base(prot.KeyID) // everything after last '/' if k, err := s.db.accountKey(ctx, path.Base(prot.KeyID)); err == nil {
k, err := s.db.accountKey(ctx, id) key = k
if err != nil { } else {
http.Error(w, "unknown kid", http.StatusUnauthorized) http.Error(w, "unknown kid", http.StatusUnauthorized)
return nil, false return nil, false
} }
key = k default:
}
if key == nil {
http.Error(w, "no verification key", http.StatusBadRequest) http.Error(w, "no verification key", http.StatusBadRequest)
return nil, false return nil, false
} }
@@ -468,7 +478,6 @@ func (s *server) verifyJWS(ctx context.Context, w http.ResponseWriter, r *http.R
http.Error(w, "signature invalid", http.StatusUnauthorized) http.Error(w, "signature invalid", http.StatusUnauthorized)
return nil, false return nil, false
} }
return &jwsPayload{Data: payload, JWK: key}, true return &jwsPayload{Data: payload, JWK: key}, true
} }
@@ -489,6 +498,9 @@ func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Replay-Nonce", n) w.Header().Set("Replay-Nonce", n)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
case r.Method == http.MethodGet && r.URL.Path == "/directory":
s.handleDirectory(w, r)
case r.Method == http.MethodPost && r.URL.Path == "/acme/new-account": case r.Method == http.MethodPost && r.URL.Path == "/acme/new-account":
s.handleNewAccount(ctx, w, r) s.handleNewAccount(ctx, w, r)
@@ -501,9 +513,6 @@ func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case r.Method == http.MethodPost && r.URL.Path == "/acme/revoke-cert": case r.Method == http.MethodPost && r.URL.Path == "/acme/revoke-cert":
s.handleRevoke(ctx, w, r) s.handleRevoke(ctx, w, r)
case r.Method == http.MethodGet && r.URL.Path == "/acme/directory":
s.handleDirectory(w, r)
case r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/acme/order/"): case r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/acme/order/"):
s.handleGetOrder(ctx, w, r) s.handleGetOrder(ctx, w, r)