{"record":{"id":"4ce0b03499610420","repo":"apereo/cas","slug":"invalid-signature","errorCode":null,"errorMessage":"Invalid signature","messagePattern":"Invalid signature","errorType":"http","errorClass":"ResponseStatusException","httpStatus":401,"severity":"error","filePath":"support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/jwks/OidcJwksRegistrationEndpointController.java","lineNumber":114,"sourceCode":"            .build();\n        val accessResult = configurationContext.getRegisteredServiceAccessStrategyEnforcer().execute(audit);\n        accessResult.throwExceptionIfNeeded();\n        \n        val jws = JWSObject.parse(registrationRequest.proof());\n\n        val alg = jws.getHeader().getAlgorithm();\n        FunctionUtils.throwIf(!JWSAlgorithm.Family.EC.contains(alg) && !JWSAlgorithm.Family.RSA.contains(alg) && !JWSAlgorithm.EdDSA.equals(alg),\n            () -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, \"Invalid algorithm: \" + alg));\n\n        val jwk = jws.getHeader().getJWK();\n        val verifier = switch (jwk) {\n            case ECKey ecKey -> new ECDSAVerifier(ecKey);\n            case RSAKey rsaKey -> new RSASSAVerifier(rsaKey);\n            case OctetKeyPair okp -> new Ed25519Verifier(okp.toPublicJWK());\n            default -> throw new IllegalArgumentException(\"Unsupported key type: \" + jwk.getKeyType());\n        };\n        if (!jws.verify((JWSVerifier) verifier)) {\n            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, \"Invalid signature\");\n        }\n        val jkt = jwk.computeThumbprint().toString();\n        clientJwksRegistrationStore.save(accessTokenTicket.getClientId(), jkt, jwk.toPublicJWK().toJSONString());\n        return ResponseEntity.ok(new ClientJwksRegistrationResponse(jkt));\n    }\n\n    /**\n     * Handle errors.\n     *\n     * @param ex the ex\n     * @return the response entity\n     */\n    @ExceptionHandler(Exception.class)\n    @SuppressWarnings(\"UnusedMethod\")\n    private static ResponseEntity<String> handle(final Exception ex) {\n        LoggingUtils.error(LOGGER, ex);\n        if (ex instanceof final ResponseStatusException rse) {\n            return ResponseEntity.status(rse.getStatusCode()).body(rse.getReason());","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/jwks/OidcJwksRegistrationEndpointController.java#L96-L132","documentation":"After selecting the appropriate verifier for the embedded JWK, the controller verifies the JWS signature of the client JWKS registration request. If verification fails, the request is rejected with HTTP 401 'Invalid signature', since the request cannot be attributed to the holder of the registered key.","triggerScenarios":"handleRegistration(): jws.verify(verifier) returns false — the JWT was signed by a different key than the one embedded/claimed, the payload was modified after signing, or the wrong algorithm/key material was used by the client.","commonSituations":"Signing with the private key of a different keypair than the embedded public JWK; serializing the JWT in a way that alters the payload after signing; mismatched algorithm between header and actual signature; key rotation on the client mid-request.","solutions":["Re-sign the registration JWT with the private key matching the embedded public JWK and sign again after finalizing the payload","Confirm the JWS algorithm in the header matches the key type and the signer used","Regenerate the keypair and rebuild the signed request from scratch to rule out payload tampering/encoding issues"],"exampleFix":"// before: signing with mismatched key\nJWSSigner signer = new RSASSASigner(otherRsaKey);\n// after: sign with private key of the embedded JWK\nJWSSigner signer = new RSASSASigner(embeddedRsaJwk.toRSAPrivateKey());","handlingStrategy":"validation","validationCode":"JWSSigner signer = ...; // key matching the embedded JWK\nSignedJWT jws = new SignedJWT(header, claims);\njws.sign(signer);\nif (!jws.verify(verifierForEmbeddedJwk)) {\n    throw new IllegalStateException(\"JWS does not verify against embedded JWK\");\n}","typeGuard":null,"tryCatchPattern":"try { controller.handleRegistration(request, response); }\ncatch (ResponseStatusException e) {\n    if (e.getStatusCode() == HttpStatus.UNAUTHORIZED && \"Invalid signature\".equals(e.getReason())) { /* re-sign with the correct private key */ }\n    else throw e;\n}","preventionTips":["Always sign with the private key corresponding to the embedded public JWK","Sign only after the payload is final; never mutate claims post-signing","Add a local verify() step before sending the request"],"tags":["oidc","jwks","jws","signature-verification","http-401"],"backgroundTag":"jwt-signature-verification-failed","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}