From 9b36a801d638593a9c144a9726e2c361aa1edecf Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 20 Jun 2024 14:22:00 +0100 Subject: [PATCH] Require that both RS256 and ES256 must be supported if the signature algorithm is not configured --- spec/src/main/asciidoc/configuration.asciidoc | 3 +- .../jwt/tck/util/MpJwtTestVersion.java | 2 +- .../jwt/tck/config/RS256OrES256Endpoint.java | 62 +++++++++ .../RsaAndEcSignatureAlgorithmTest.java | 123 ++++++++++++++++++ .../microprofile-config-rsa-ec.properties | 22 ++++ tck/src/test/resources/rs256es256.jwk | 21 +++ .../test/resources/suites/tck-base-suite.xml | 1 + .../test/resources/suites/tck-full-suite.xml | 1 + 8 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 tck/src/test/java/org/eclipse/microprofile/jwt/tck/config/RS256OrES256Endpoint.java create mode 100644 tck/src/test/java/org/eclipse/microprofile/jwt/tck/config/RsaAndEcSignatureAlgorithmTest.java create mode 100644 tck/src/test/resources/META-INF/microprofile-config-rsa-ec.properties create mode 100644 tck/src/test/resources/rs256es256.jwk diff --git a/spec/src/main/asciidoc/configuration.asciidoc b/spec/src/main/asciidoc/configuration.asciidoc index b90867f..413db01 100644 --- a/spec/src/main/asciidoc/configuration.asciidoc +++ b/spec/src/main/asciidoc/configuration.asciidoc @@ -356,7 +356,8 @@ return Public Key text in one of the supported formats. #### `mp.jwt.verify.publickey.algorithm` The `mp.jwt.verify.publickey.algorithm` configuration property allows for specifying which Public Key Signature Algorithm -is supported by the MP JWT endpoint. This property can be be set to either `RS256` or `ES256`. Default value is `RS256`. +is supported by the MP JWT endpoint. This property can be be set to either `RS256` or `ES256`. If `mp.jwt.verify.publickey.algorithm` is not set then both `RS256` and `ES256` must be accepted. + Support for the other asymmetric signature algorithms such as `RS512`, `ES512` and others is optional. `mp.jwt.verify.publickey.algorithm` will provide an additional hint how to read the Public Key in the PKCS#8 PEM format as both RSA and EC Public Keys in the PKCS#8 PEM format may only have a standard `-----BEGIN PUBLIC KEY-----` header and footer. diff --git a/tck/src/main/java/org/eclipse/microprofile/jwt/tck/util/MpJwtTestVersion.java b/tck/src/main/java/org/eclipse/microprofile/jwt/tck/util/MpJwtTestVersion.java index 7a3d4fc..9f9ef16 100644 --- a/tck/src/main/java/org/eclipse/microprofile/jwt/tck/util/MpJwtTestVersion.java +++ b/tck/src/main/java/org/eclipse/microprofile/jwt/tck/util/MpJwtTestVersion.java @@ -24,7 +24,7 @@ * loading the META-INF/MPJWTTESTVERSION resource from the test war and converting it to the MpJwtTestVersion value. */ public enum MpJwtTestVersion { - MPJWT_V_1_0, MPJWT_V_1_1, MPJWT_V_1_2, MPJWT_V_2_1; + MPJWT_V_1_0, MPJWT_V_1_1, MPJWT_V_1_2, MPJWT_V_2_1, MPJWT_V_2_2; public static final String VERSION_LOCATION = "META-INF/MPJWTTESTVERSION"; public static final String MANIFEST_NAME = "MPJWTTESTVERSION"; diff --git a/tck/src/test/java/org/eclipse/microprofile/jwt/tck/config/RS256OrES256Endpoint.java b/tck/src/test/java/org/eclipse/microprofile/jwt/tck/config/RS256OrES256Endpoint.java new file mode 100644 index 0000000..43a1ba6 --- /dev/null +++ b/tck/src/test/java/org/eclipse/microprofile/jwt/tck/config/RS256OrES256Endpoint.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016-2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.eclipse.microprofile.jwt.tck.config; + +import java.io.StringReader; + +import org.eclipse.microprofile.jwt.Claim; +import org.eclipse.microprofile.jwt.ClaimValue; +import org.eclipse.microprofile.jwt.Claims; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonReader; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +/** + * The common endpoint used by the various config tests + */ +@RequestScoped +@Path("/endp") +public class RS256OrES256Endpoint { + @Inject + @Claim(standard = Claims.raw_token) + private ClaimValue rawToken; + + @GET + @Path("/verifyToken") + @Produces(MediaType.TEXT_PLAIN) + @RolesAllowed("Tester") + public String verifyRS256Token() { + return getAlgorithm(); + } + + private String getAlgorithm() { + JsonReader jsonReader = Json.createReader(new StringReader(rawToken.getValue().split(".")[0])); + JsonObject headers = jsonReader.readObject(); + return headers.getString("alg"); + } +} diff --git a/tck/src/test/java/org/eclipse/microprofile/jwt/tck/config/RsaAndEcSignatureAlgorithmTest.java b/tck/src/test/java/org/eclipse/microprofile/jwt/tck/config/RsaAndEcSignatureAlgorithmTest.java new file mode 100644 index 0000000..9fa0810 --- /dev/null +++ b/tck/src/test/java/org/eclipse/microprofile/jwt/tck/config/RsaAndEcSignatureAlgorithmTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2016-2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.eclipse.microprofile.jwt.tck.config; + +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; +import static org.eclipse.microprofile.jwt.tck.TCKConstants.TEST_GROUP_CONFIG; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.PrivateKey; + +import org.eclipse.microprofile.jwt.tck.container.jaxrs.TCKApplication; +import org.eclipse.microprofile.jwt.tck.util.MpJwtTestVersion; +import org.eclipse.microprofile.jwt.tck.util.TokenUtils; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.arquillian.testng.Arquillian; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.testng.Assert; +import org.testng.Reporter; +import org.testng.annotations.Test; + +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response; + +/** + * Validate that if mp.jwt.verify.publickey.algorithm is not configured, then both RS256 and ES256 signatures must be + * accepted. + */ +public class RsaAndEcSignatureAlgorithmTest extends Arquillian { + + /** + * The base URL for the container under test + */ + @ArquillianResource + private URL baseURL; + + /** + * Create a CDI aware base web application archive + * + * @return the base base web application archive + * @throws IOException + * - on resource failure + */ + @Deployment() + public static WebArchive createDeployment() throws IOException { + URL config = + RsaAndEcSignatureAlgorithmTest.class.getResource("/META-INF/microprofile-config-rsa-ec.properties"); + + WebArchive webArchive = ShrinkWrap + .create(WebArchive.class, "RsaAndEcSignatureAlgorithmTest.war") + .addAsManifestResource(new StringAsset(MpJwtTestVersion.MPJWT_V_2_2.name()), + MpJwtTestVersion.MANIFEST_NAME) + .addClass(RS256OrES256Endpoint.class) + .addClass(TCKApplication.class) + .addClass(SimpleTokenUtils.class) + .addAsWebInfResource("beans.xml", "beans.xml") + .addAsManifestResource(config, "microprofile-config.properties"); + return webArchive; + } + + @RunAsClient + @Test(groups = TEST_GROUP_CONFIG, description = "Validate that the ES256 signed token is accepted") + public void testES256Token() throws Exception { + Reporter.log("testES256Token, expect HTTP_OK"); + + PrivateKey privateKey = TokenUtils.readECPrivateKey("/ecPrivateKey.pem"); + String kid = "eckey"; + String token = TokenUtils.signClaims(privateKey, kid, "/Token1.json"); + + String uri = baseURL.toExternalForm() + "endp/verifyToken"; + WebTarget echoEndpointTarget = ClientBuilder.newClient() + .target(uri); + Response response = + echoEndpointTarget.request(TEXT_PLAIN).header(HttpHeaders.AUTHORIZATION, "Bearer " + token).get(); + Assert.assertEquals(response.getStatus(), HttpURLConnection.HTTP_OK); + String replyString = response.readEntity(String.class); + Assert.assertEquals("ES256", replyString); + } + + @RunAsClient + @Test(groups = TEST_GROUP_CONFIG, description = "Validate that the RS256 signed token is accepted") + public void testRS256Token() throws Exception { + Reporter.log("testRS256Token, expect HTTP_OK"); + + PrivateKey privateKey = TokenUtils.readECPrivateKey("/privateKey4k.pem"); + String kid = "rskey"; + String token = TokenUtils.signClaims(privateKey, kid, "/Token1.json"); + + String uri = baseURL.toExternalForm() + "endp/verifyToken"; + WebTarget echoEndpointTarget = ClientBuilder.newClient() + .target(uri); + Response response = + echoEndpointTarget.request(TEXT_PLAIN).header(HttpHeaders.AUTHORIZATION, "Bearer " + token).get(); + Assert.assertEquals(response.getStatus(), HttpURLConnection.HTTP_OK); + String replyString = response.readEntity(String.class); + Assert.assertEquals("RS256", replyString); + } + +} diff --git a/tck/src/test/resources/META-INF/microprofile-config-rsa-ec.properties b/tck/src/test/resources/META-INF/microprofile-config-rsa-ec.properties new file mode 100644 index 0000000..f411595 --- /dev/null +++ b/tck/src/test/resources/META-INF/microprofile-config-rsa-ec.properties @@ -0,0 +1,22 @@ +# +# Copyright (c) 2011-2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# You may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# A reference to the publicKey4k.pem contents embedded location +mp.jwt.verify.publickey.location=/rs256es256.jwk +mp.jwt.verify.issuer=https://server.example.com \ No newline at end of file diff --git a/tck/src/test/resources/rs256es256.jwk b/tck/src/test/resources/rs256es256.jwk new file mode 100644 index 0000000..f549614 --- /dev/null +++ b/tck/src/test/resources/rs256es256.jwk @@ -0,0 +1,21 @@ +{ + "keys": [ + { + "kty": "RSA", + "use": "sig", + "alg": "RS256", + "kid": "rskey", + "e": "AQAB", + "n": "tL6HShqY5H4y56rsCo7VdhT9_eLQwsJpKWg66j98XsB_qc5ZxkJ25GXCzpjR0ZvzAxMNlj1hrMORaKVzz2_5axZgF1eZfzgrNyQ9rtGaBtMNAB20jLsoYp5psRTaYxKeOiLHPr3956ukSRUF9YfJGSamrvGOwC8h6zbq6uaydv-FVJXijlMD_iCggUfoirtVOWK_X1IzV7covxcGzT0X019_4RbtjLdnvqZnGqmpHQpBEItI-4gNvaKR8NDWUxAjO_v-oOKR5nEUnDWcQSCxKmyQrVJtHr9PBwWrHzTSx4k1L1hLf-AWXAdy_r6c0Lzgt5knmZTyWDG2-n8SlrXxHHxFO1Wz8H_OKBzTAf8zIuj2lkXYo-M6aoJM7qQmTys80dtYvnaHGSl-jpe2plMbS9RS4XcHH7vCqJc9acBnp9CvLgjOmA0b5Rc0WyN4sn1SDFYe6HZcVo4YGTbtTTlwgu_ozQ1x-xpTAaU0mWkHMwT0CO79rPORjhDXokEuduvtp6VUiAaoFF6Y3QQLf6O3P9p8yghpBBLb460lEQqOHQQGP0EK46cU81dlcD5lYE0TayDzb9pZZWUyjIE4ElzyW7wgI4xw7czdBalN-IhXKfGUCqIDVh7X7JpmskZMaRixf424yBcZLntEejZy59yLDSssHMc_bqnBraXuo8JBEPk" + }, + { + "kty": "EC", + "use": "sig", + "alg": "ES256", + "kid": "eckey", + "crv":"P-256", + "x":"w4HohvwOj21FBQE1PrJOAlPRQMyWimmXH9rIHa7YMTU", + "y":"osZEjUhZa79-kClcGm79eX0q_QFLlrA99MhkzNy6MtI" + } + ] +} diff --git a/tck/src/test/resources/suites/tck-base-suite.xml b/tck/src/test/resources/suites/tck-base-suite.xml index 1e35316..87d2548 100644 --- a/tck/src/test/resources/suites/tck-base-suite.xml +++ b/tck/src/test/resources/suites/tck-base-suite.xml @@ -72,6 +72,7 @@ + diff --git a/tck/src/test/resources/suites/tck-full-suite.xml b/tck/src/test/resources/suites/tck-full-suite.xml index 75e87bb..4f94ee7 100644 --- a/tck/src/test/resources/suites/tck-full-suite.xml +++ b/tck/src/test/resources/suites/tck-full-suite.xml @@ -72,6 +72,7 @@ +