You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kafka.apache.org by ju...@apache.org on 2021/11/16 00:21:59 UTC
[kafka] branch trunk updated: KAFKA-13445: Add ECDSA test for JWT validation (#11487)
This is an automated email from the ASF dual-hosted git repository.
junrao pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git
The following commit(s) were added to refs/heads/trunk by this push:
new f7031aa KAFKA-13445: Add ECDSA test for JWT validation (#11487)
f7031aa is described below
commit f7031aa8073097e806d011b1aacf967e2a173a9c
Author: Kirk True <ki...@mustardgrain.com>
AuthorDate: Mon Nov 15 16:20:27 2021 -0800
KAFKA-13445: Add ECDSA test for JWT validation (#11487)
Reviewers: Jun Rao <ju...@gmail.com>
---
.../oauthbearer/secured/AccessTokenBuilder.java | 61 +++++++++-------------
.../OAuthBearerLoginCallbackHandlerTest.java | 5 +-
.../oauthbearer/secured/OAuthBearerTest.java | 24 +++++++++
.../OAuthBearerValidatorCallbackHandlerTest.java | 8 ++-
.../secured/ValidatorAccessTokenValidatorTest.java | 33 ++++++++----
5 files changed, 83 insertions(+), 48 deletions(-)
diff --git a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/AccessTokenBuilder.java b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/AccessTokenBuilder.java
index 20def92..24a40aa 100644
--- a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/AccessTokenBuilder.java
+++ b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/AccessTokenBuilder.java
@@ -24,9 +24,7 @@ import java.io.IOException;
import java.util.Collection;
import org.apache.kafka.common.utils.MockTime;
import org.apache.kafka.common.utils.Time;
-import org.jose4j.jwk.RsaJsonWebKey;
-import org.jose4j.jwk.RsaJwkGenerator;
-import org.jose4j.jws.AlgorithmIdentifiers;
+import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.ReservedClaimNames;
import org.jose4j.lang.JoseException;
@@ -35,40 +33,40 @@ public class AccessTokenBuilder {
private final ObjectMapper objectMapper = new ObjectMapper();
+ private String alg;
+
private String audience;
private String subject = "jdoe";
- private String subjectClaimName = ReservedClaimNames.SUBJECT;
+ private final String subjectClaimName = ReservedClaimNames.SUBJECT;
private Object scope = "engineering";
- private String scopeClaimName = "scope";
+ private final String scopeClaimName = "scope";
- private Long issuedAtSeconds;
+ private final Long issuedAtSeconds;
private Long expirationSeconds;
- private RsaJsonWebKey jwk;
+ private PublicJsonWebKey jwk;
- public AccessTokenBuilder() throws JoseException {
+ public AccessTokenBuilder() {
this(new MockTime());
}
- public AccessTokenBuilder(Time time) throws JoseException {
+ public AccessTokenBuilder(Time time) {
this.issuedAtSeconds = time.milliseconds() / 1000;
this.expirationSeconds = this.issuedAtSeconds + 60;
- this.jwk = createJwk();
}
- public static RsaJsonWebKey createJwk() throws JoseException {
- RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk(2048);
- jwk.setKeyId("key-1");
- return jwk;
+ public String alg() {
+ return alg;
}
- public String audience() {
- return audience;
+ public AccessTokenBuilder alg(String alg) {
+ this.alg = alg;
+ return this;
}
public AccessTokenBuilder audience(String audience) {
@@ -89,11 +87,6 @@ public class AccessTokenBuilder {
return subjectClaimName;
}
- public AccessTokenBuilder subjectClaimName(String subjectClaimName) {
- this.subjectClaimName = subjectClaimName;
- return this;
- }
-
public Object scope() {
return scope;
}
@@ -118,20 +111,10 @@ public class AccessTokenBuilder {
return scopeClaimName;
}
- public AccessTokenBuilder scopeClaimName(String scopeClaimName) {
- this.scopeClaimName = scopeClaimName;
- return this;
- }
-
public Long issuedAtSeconds() {
return issuedAtSeconds;
}
- public AccessTokenBuilder issuedAtSeconds(Long issuedAtSeconds) {
- this.issuedAtSeconds = issuedAtSeconds;
- return this;
- }
-
public Long expirationSeconds() {
return expirationSeconds;
}
@@ -141,11 +124,11 @@ public class AccessTokenBuilder {
return this;
}
- public RsaJsonWebKey jwk() {
+ public PublicJsonWebKey jwk() {
return jwk;
}
- public AccessTokenBuilder jwk(RsaJsonWebKey jwk) {
+ public AccessTokenBuilder jwk(PublicJsonWebKey jwk) {
this.jwk = jwk;
return this;
}
@@ -183,9 +166,15 @@ public class AccessTokenBuilder {
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(json);
- jws.setKey(jwk.getPrivateKey());
- jws.setKeyIdHeaderValue(jwk.getKeyId());
- jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
+
+ if (jwk != null) {
+ jws.setKey(jwk.getPrivateKey());
+ jws.setKeyIdHeaderValue(jwk.getKeyId());
+ }
+
+ if (alg != null)
+ jws.setAlgorithmHeaderValue(alg);
+
return jws.getCompactSerialization();
}
diff --git a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerLoginCallbackHandlerTest.java b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerLoginCallbackHandlerTest.java
index 4be823e..ab823a3 100644
--- a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerLoginCallbackHandlerTest.java
+++ b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerLoginCallbackHandlerTest.java
@@ -40,6 +40,7 @@ import org.apache.kafka.common.security.oauthbearer.OAuthBearerToken;
import org.apache.kafka.common.security.oauthbearer.OAuthBearerTokenCallback;
import org.apache.kafka.common.security.oauthbearer.internals.OAuthBearerClientInitialResponse;
import org.apache.kafka.common.utils.Utils;
+import org.jose4j.jws.AlgorithmIdentifiers;
import org.junit.jupiter.api.Test;
public class OAuthBearerLoginCallbackHandlerTest extends OAuthBearerTest {
@@ -47,7 +48,9 @@ public class OAuthBearerLoginCallbackHandlerTest extends OAuthBearerTest {
@Test
public void testHandleTokenCallback() throws Exception {
Map<String, ?> configs = getSaslConfigs();
- AccessTokenBuilder builder = new AccessTokenBuilder();
+ AccessTokenBuilder builder = new AccessTokenBuilder()
+ .jwk(createRsaJwk())
+ .alg(AlgorithmIdentifiers.RSA_USING_SHA256);
String accessToken = builder.build();
AccessTokenRetriever accessTokenRetriever = () -> accessToken;
diff --git a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerTest.java b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerTest.java
index 6fec08d..5edb0b0 100644
--- a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerTest.java
+++ b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerTest.java
@@ -45,6 +45,10 @@ import org.apache.kafka.common.security.auth.AuthenticateCallbackHandler;
import org.apache.kafka.common.security.authenticator.TestJaasConfig;
import org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule;
import org.apache.kafka.common.utils.Utils;
+import org.jose4j.jwk.PublicJsonWebKey;
+import org.jose4j.jwk.RsaJsonWebKey;
+import org.jose4j.jwk.RsaJwkGenerator;
+import org.jose4j.lang.JoseException;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.function.Executable;
@@ -195,4 +199,24 @@ public abstract class OAuthBearerTest {
return getSaslConfigs(Collections.emptyMap());
}
+ protected PublicJsonWebKey createRsaJwk() throws JoseException {
+ RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk(2048);
+ jwk.setKeyId("key-1");
+ return jwk;
+ }
+
+ protected PublicJsonWebKey createEcJwk() throws JoseException {
+ PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk("{" +
+ " \"kty\": \"EC\"," +
+ " \"d\": \"Tk7qzHNnSBMioAU7NwZ9JugFWmWbUCyzeBRjVcTp_so\"," +
+ " \"use\": \"sig\"," +
+ " \"crv\": \"P-256\"," +
+ " \"kid\": \"key-1\"," +
+ " \"x\": \"qqeGjWmYZU5M5bBrRw1zqZcbPunoFVxsfaa9JdA0R5I\"," +
+ " \"y\": \"wnoj0YjheNP80XYh1SEvz1-wnKByEoHvb6KrDcjMuWc\"" +
+ "}");
+ jwk.setKeyId("key-1");
+ return jwk;
+ }
+
}
\ No newline at end of file
diff --git a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerValidatorCallbackHandlerTest.java b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerValidatorCallbackHandlerTest.java
index 326197d..67e2a8b 100644
--- a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerValidatorCallbackHandlerTest.java
+++ b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/OAuthBearerValidatorCallbackHandlerTest.java
@@ -31,6 +31,7 @@ import javax.security.auth.callback.Callback;
import org.apache.kafka.common.security.oauthbearer.OAuthBearerToken;
import org.apache.kafka.common.security.oauthbearer.OAuthBearerValidatorCallback;
import org.apache.kafka.common.utils.Utils;
+import org.jose4j.jws.AlgorithmIdentifiers;
import org.junit.jupiter.api.Test;
public class OAuthBearerValidatorCallbackHandlerTest extends OAuthBearerTest {
@@ -39,7 +40,10 @@ public class OAuthBearerValidatorCallbackHandlerTest extends OAuthBearerTest {
public void testBasic() throws Exception {
String expectedAudience = "a";
List<String> allAudiences = Arrays.asList(expectedAudience, "b", "c");
- AccessTokenBuilder builder = new AccessTokenBuilder().audience(expectedAudience);
+ AccessTokenBuilder builder = new AccessTokenBuilder()
+ .audience(expectedAudience)
+ .jwk(createRsaJwk())
+ .alg(AlgorithmIdentifiers.RSA_USING_SHA256);
String accessToken = builder.build();
Map<String, ?> configs = getSaslConfigs(SASL_OAUTHBEARER_EXPECTED_AUDIENCE, allAudiences);
@@ -92,7 +96,7 @@ public class OAuthBearerValidatorCallbackHandlerTest extends OAuthBearerTest {
AccessTokenBuilder builder) {
OAuthBearerValidatorCallbackHandler handler = new OAuthBearerValidatorCallbackHandler();
CloseableVerificationKeyResolver verificationKeyResolver = (jws, nestingContext) ->
- builder.jwk().getRsaPublicKey();
+ builder.jwk().getPublicKey();
AccessTokenValidator accessTokenValidator = AccessTokenValidatorFactory.create(options, verificationKeyResolver);
handler.init(verificationKeyResolver, accessTokenValidator);
return handler;
diff --git a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidatorTest.java b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidatorTest.java
index 76333e3..a481988 100644
--- a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidatorTest.java
+++ b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidatorTest.java
@@ -19,8 +19,9 @@ package org.apache.kafka.common.security.oauthbearer.secured;
import java.util.Collections;
import org.apache.kafka.common.security.oauthbearer.OAuthBearerToken;
+import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.jws.AlgorithmIdentifiers;
-import org.jose4j.jws.JsonWebSignature;
+import org.jose4j.lang.InvalidAlgorithmException;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -38,16 +39,30 @@ public class ValidatorAccessTokenValidatorTest extends AccessTokenValidatorTest
}
@Test
- public void testBasicEncryption() throws Exception {
- AccessTokenBuilder builder = new AccessTokenBuilder();
- AccessTokenValidator validator = createAccessTokenValidator(builder);
+ public void testRsaEncryptionAlgorithm() throws Exception {
+ PublicJsonWebKey jwk = createRsaJwk();
+ testEncryptionAlgorithm(jwk, AlgorithmIdentifiers.RSA_USING_SHA256);
+ }
- JsonWebSignature jws = new JsonWebSignature();
- jws.setKey(builder.jwk().getPrivateKey());
- jws.setKeyIdHeaderValue(builder.jwk().getKeyId());
- jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
- String accessToken = builder.build();
+ @Test
+ public void testEcdsaEncryptionAlgorithm() throws Exception {
+ PublicJsonWebKey jwk = createEcJwk();
+ testEncryptionAlgorithm(jwk, AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256);
+ }
+ @Test
+ public void testInvalidEncryptionAlgorithm() throws Exception {
+ PublicJsonWebKey jwk = createRsaJwk();
+
+ assertThrowsWithMessage(InvalidAlgorithmException.class,
+ () -> testEncryptionAlgorithm(jwk, "fake"),
+ "fake is an unknown, unsupported or unavailable alg algorithm");
+ }
+
+ private void testEncryptionAlgorithm(PublicJsonWebKey jwk, String alg) throws Exception {
+ AccessTokenBuilder builder = new AccessTokenBuilder().jwk(jwk).alg(alg);
+ AccessTokenValidator validator = createAccessTokenValidator(builder);
+ String accessToken = builder.build();
OAuthBearerToken token = validator.validate(accessToken);
assertEquals(builder.subject(), token.principalName());