{"record":{"id":"e9a6fd4721c74d9e","repo":"ory/hydra","slug":"priv-pub-jwk-key-mismatch","errorCode":null,"errorMessage":"priv/pub JWK key mismatch","messagePattern":"priv/pub JWK key mismatch","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/josex/utils.go","lineNumber":39,"sourceCode":"\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/go-jose/go-jose/v3\"\n)\n\n// LoadJSONWebKey returns a *jose.JSONWebKey for a given JSON string.\nfunc LoadJSONWebKey(json []byte, pub bool) (*jose.JSONWebKey, error) {\n\tvar jwk jose.JSONWebKey\n\terr := jwk.UnmarshalJSON(json)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !jwk.Valid() {\n\t\treturn nil, errors.New(\"invalid JWK key\")\n\t}\n\tif jwk.IsPublic() != pub {\n\t\treturn nil, errors.New(\"priv/pub JWK key mismatch\")\n\t}\n\treturn &jwk, nil\n}\n\n// LoadPublicKey loads a public key from PEM/DER/JWK-encoded data.\nfunc LoadPublicKey(data []byte) (interface{}, error) {\n\tinput := data\n\n\tblock, _ := pem.Decode(data)\n\tif block != nil {\n\t\tinput = block.Bytes\n\t}\n\n\t// Try to load SubjectPublicKeyInfo\n\tpub, err0 := x509.ParsePKIXPublicKey(input)\n\tif err0 == nil {\n\t\treturn pub, nil\n\t}","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/josex/utils.go#L21-L57","documentation":"LoadJSONWebKey (oryx/josex/utils.go:39) parses a JSON Web Key and, after validating it, checks that the key's public/private character matches the `pub` flag the caller passed. `jwk.IsPublic()` reports whether the JWK contains only public members (no \"d\" private exponent etc.). The error means the caller asked for, say, a public key but the JSON contained private key material — or vice versa.","triggerScenarios":"Calling LoadJSONWebKey(json, true) with a JWK that contains private components (e.g. RSA \"d\", EC \"d\"), or calling LoadJSONWebKey(json, false) with a public-only JWK. Since LoadPublicKey calls LoadJSONWebKey(data, true), feeding a private JWK to LoadPublicKey also triggers it (surfaced wrapped in the 'parse error' message); same for LoadPrivateKey(data) with a public JWK.","commonSituations":"Config files or environment variables holding a JWK where someone pasted the public half where the private half is expected (e.g. signing key config) or the opposite (verification key config); key-rotation tooling exporting the wrong half of a keypair; copying the JWKS public keys into a private-key setting.","solutions":["Check which half of the keypair your config expects: for LoadPublicKey/verification use the public JWK; for LoadPrivateKey/signing use the private JWK (one containing \"d\").","Inspect the raw JWK JSON: presence of the \"d\" member means it is private; absence means public.","Re-export the correct key half from your key management system or with `go-jose` / openssl and update the configuration.","If you only need the public part and only have the private JWK, derive the public key from the private one (e.g. x509/ECDSA/RSA public of the parsed private key) instead of passing the private JWK to LoadPublicKey."],"exampleFix":"// before: private JWK fed to LoadPublicKey -> \"priv/pub JWK key mismatch\"\npub, err := josex.LoadPublicKey([]byte(privateJWKJSON))\n\n// after: export the public half first\nvar priv jose.JSONWebKey\n_ = priv.UnmarshalJSON([]byte(privateJWKJSON))\npubJWK := priv.Public() // strips private members\npub, err := josex.LoadPublicKey(mustMarshalJSON(pubJWK))","handlingStrategy":"validation","validationCode":"func isPublicJWK(jwkJSON []byte) bool {\n    var raw struct{ D string `json:\"d\"` }\n    return json.Unmarshal(jwkJSON, &raw) == nil && raw.D == \"\"\n}\n// call LoadPublicKey only when isPublicJWK(data), LoadPrivateKey only when !isPublicJWK(data)","typeGuard":"func assertKeyHalfMatches(jwkJSON []byte, wantPublic bool) error {\n    var jwk jose.JSONWebKey\n    if err := jwk.UnmarshalJSON(jwkJSON); err != nil { return err }\n    if !jwk.Valid() { return errors.New(\"invalid JWK\") }\n    if jwk.IsPublic() != wantPublic { return errors.New(\"priv/pub JWK key mismatch\") }\n    return nil\n}","tryCatchPattern":null,"preventionTips":["Name config fields explicitly (public_key_jwk vs private_key_jwk) so operators paste the right half.","Check for the \"d\" member in stored JWKs as part of config validation at startup.","Fail fast at boot with a clear message instead of lazily parsing keys per request.","Keep public JWKS and private signing keys in separate secret paths."],"tags":["jwk","crypto","key-mismatch","configuration"],"backgroundTag":"jwk-priv-pub-mismatch","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}