diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f2bceefd19..afa797e716 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -85,7 +85,7 @@ jobs:
SEP=","
done
- ./mvnw test -pl "$PROJECTS" -am
+ ./mvnw install -pl "$PROJECTS" -am
- name: Upload JVM Heapdumps
if: always()
diff --git a/authz/client/pom.xml b/authz/client/pom.xml
index a9ac2de75c..08b4388059 100644
--- a/authz/client/pom.xml
+++ b/authz/client/pom.xml
@@ -18,6 +18,12 @@
Keycloak Authz: Client API. This module is supposed to be used just in the Keycloak repository for the testsuite. It is NOT supposed to be used by the 3rd party applications.
For the use by 3rd party applications, please use `org.keycloak:keycloak-authz-client` module.
+
+ 8
+ 8
+ 8
+
+
org.keycloak
diff --git a/common/pom.xml b/common/pom.xml
index 5f9f4b628e..1fea851011 100755
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -32,8 +32,9 @@
Common library and dependencies shared with server and all adapters
-
- 11
+ 8
+ 8
+ 8
${maven.build.timestamp}
yyyy-MM-dd HH:mm
diff --git a/common/src/main/java/org/keycloak/common/util/reflections/Reflections.java b/common/src/main/java/org/keycloak/common/util/reflections/Reflections.java
index 1ff98e9be2..2dbffd55c3 100644
--- a/common/src/main/java/org/keycloak/common/util/reflections/Reflections.java
+++ b/common/src/main/java/org/keycloak/common/util/reflections/Reflections.java
@@ -988,7 +988,7 @@ public class Reflections {
* @throws InstantiationException
* @deprecated for removal in Keycloak 27
*/
- @Deprecated(forRemoval = true)
+ @Deprecated
public static T newInstance(final Class fromClass) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return newInstance(fromClass, fromClass.getName());
}
@@ -1008,7 +1008,7 @@ public class Reflections {
* @throws InstantiationException
* @deprecated for removal in Keycloak 27
*/
- @Deprecated(forRemoval = true)
+ @Deprecated
public static T newInstance(final Class> type, final String fullQualifiedName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return (T) classForName(fullQualifiedName, type.getClassLoader()).newInstance();
}
diff --git a/common/src/main/java/org/keycloak/common/util/reflections/SetAccessiblePrivilegedAction.java b/common/src/main/java/org/keycloak/common/util/reflections/SetAccessiblePrivilegedAction.java
index 62ded30753..db7379cf6e 100644
--- a/common/src/main/java/org/keycloak/common/util/reflections/SetAccessiblePrivilegedAction.java
+++ b/common/src/main/java/org/keycloak/common/util/reflections/SetAccessiblePrivilegedAction.java
@@ -24,7 +24,7 @@ import java.security.PrivilegedAction;
* A {@link java.security.PrivilegedAction} that calls {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
* @deprecated for removal in Keycloak 27
*/
-@Deprecated(forRemoval = true)
+@Deprecated
public class SetAccessiblePrivilegedAction implements PrivilegedAction {
private final AccessibleObject member;
diff --git a/common/src/main/java/org/keycloak/common/util/reflections/UnSetAccessiblePrivilegedAction.java b/common/src/main/java/org/keycloak/common/util/reflections/UnSetAccessiblePrivilegedAction.java
index 01e7096247..f697eb690a 100644
--- a/common/src/main/java/org/keycloak/common/util/reflections/UnSetAccessiblePrivilegedAction.java
+++ b/common/src/main/java/org/keycloak/common/util/reflections/UnSetAccessiblePrivilegedAction.java
@@ -24,7 +24,7 @@ import java.security.PrivilegedAction;
* A {@link PrivilegedAction} that calls {@link AccessibleObject#setAccessible(boolean)}
* @deprecated for removal in Keycloak 27
*/
-@Deprecated(forRemoval = true)
+@Deprecated
public class UnSetAccessiblePrivilegedAction implements PrivilegedAction {
private final AccessibleObject member;
diff --git a/core/pom.xml b/core/pom.xml
index fc63273305..2c58af77ab 100755
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -32,6 +32,9 @@
+ 8
+ 8
+ 8
${maven.build.timestamp}
yyyy-MM-dd HH:mm
@@ -72,6 +75,38 @@
+
+
+ jdk-16
+
+ [16,)
+
+
+
+
+ maven-compiler-plugin
+
+
+ compile-java16
+ compile
+
+ compile
+
+
+ 16
+
+ ${project.basedir}/src/main/java16
+
+ true
+
+
+
+
+
+
+
+
+
diff --git a/core/src/main/java/org/keycloak/jose/jwk/JWKBuilder.java b/core/src/main/java/org/keycloak/jose/jwk/JWKBuilder.java
index c3943c742a..77de2785a9 100644
--- a/core/src/main/java/org/keycloak/jose/jwk/JWKBuilder.java
+++ b/core/src/main/java/org/keycloak/jose/jwk/JWKBuilder.java
@@ -17,19 +17,10 @@
package org.keycloak.jose.jwk;
-import java.math.BigInteger;
-import java.security.Key;
-import java.security.interfaces.EdECPublicKey;
-import java.security.spec.EdECPoint;
-import java.util.Arrays;
-import java.util.Optional;
-
-import org.keycloak.common.util.Base64Url;
-import org.keycloak.common.util.KeyUtils;
-import org.keycloak.crypto.Algorithm;
-import org.keycloak.crypto.KeyType;
import org.keycloak.crypto.KeyUse;
+import java.security.Key;
+
/**
* @author Stian Thorgersen
*/
@@ -54,55 +45,13 @@ public class JWKBuilder extends AbstractJWKBuilder {
@Override
public JWK okp(Key key) {
- return okp(key, DEFAULT_PUBLIC_KEY_USE);
+ // not supported if jdk vesion < 17
+ throw new UnsupportedOperationException("EdDSA algorithms not supported in this JDK version");
}
@Override
public JWK okp(Key key, KeyUse keyUse) {
- EdECPublicKey eddsaPublicKey = (EdECPublicKey) key;
-
- OKPPublicJWK k = new OKPPublicJWK();
-
- String kid = this.kid != null ? this.kid : KeyUtils.createKeyId(key);
-
- k.setKeyId(kid);
- k.setKeyType(KeyType.OKP);
- k.setAlgorithm(algorithm);
- k.setPublicKeyUse(keyUse == null ? DEFAULT_PUBLIC_KEY_USE.getSpecName() : keyUse.getSpecName());
- k.setCrv(eddsaPublicKey.getParams().getName());
-
- Optional x = edPublicKeyInJwkRepresentation(eddsaPublicKey);
- k.setX(x.orElse(""));
-
- return k;
+ // not supported if jdk version < 17
+ throw new UnsupportedOperationException("EdDSA algorithms not supported in this JDK version");
}
-
- private Optional edPublicKeyInJwkRepresentation(EdECPublicKey eddsaPublicKey) {
- EdECPoint edEcPoint = eddsaPublicKey.getPoint();
- BigInteger yCoordinate = edEcPoint.getY();
-
- // JWK representation "x" of a public key
- int bytesLength = 0;
- if (Algorithm.Ed25519.equals(eddsaPublicKey.getParams().getName())) {
- bytesLength = 32;
- } else if (Algorithm.Ed448.equals(eddsaPublicKey.getParams().getName())) {
- bytesLength = 57;
- } else {
- return Optional.ofNullable(null);
- }
-
- // consider the case where yCoordinate.toByteArray() is less than bytesLength due to relatively small value of y-coordinate.
- byte[] yCoordinateLittleEndianBytes = new byte[bytesLength];
-
- // convert big endian representation of BigInteger to little endian representation of JWK representation (RFC 8032,8027)
- yCoordinateLittleEndianBytes = Arrays.copyOf(reverseBytes(yCoordinate.toByteArray()), bytesLength);
-
- // set a parity of x-coordinate to the most significant bit of the last octet (RFC 8032, 8037)
- if (edEcPoint.isXOdd()) {
- yCoordinateLittleEndianBytes[yCoordinateLittleEndianBytes.length - 1] |= -128; // 0b10000000
- }
-
- return Optional.ofNullable(Base64Url.encode(yCoordinateLittleEndianBytes));
- }
-
}
diff --git a/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java b/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
index cfa646cbbd..10fe8f4526 100755
--- a/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
+++ b/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
@@ -17,18 +17,6 @@
package org.keycloak.jose.jwk;
-import java.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.spec.EdECPoint;
-import java.security.spec.EdECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.NamedParameterSpec;
-
-import org.keycloak.common.util.Base64Url;
-import org.keycloak.crypto.Algorithm;
-import org.keycloak.crypto.KeyType;
import org.keycloak.util.JsonSerialization;
/**
@@ -60,68 +48,4 @@ public class JWKParser extends AbstractJWKParser {
}
}
- @Override
- public PublicKey toPublicKey() {
- if (jwk == null) {
- throw new IllegalStateException("Not possible to convert to the publicKey. The jwk is not set");
- }
- String keyType = jwk.getKeyType();
- if (KeyType.RSA.equals(keyType)) {
- return createRSAPublicKey();
- } else if (KeyType.EC.equals(keyType)) {
- return createECPublicKey();
- } else if (KeyType.OKP.equals(keyType)) {
- return createOKPPublicKey();
- } else {
- throw new RuntimeException("Unsupported keyType " + keyType);
- }
- }
-
- private PublicKey createOKPPublicKey() {
- String x = (String) jwk.getOtherClaims().get(OKPPublicJWK.X);
- String crv = (String) jwk.getOtherClaims().get(OKPPublicJWK.CRV);
- // JWK representation "x" of a public key
- int bytesLength = 0;
- if (Algorithm.Ed25519.equals(crv)) {
- bytesLength = 32;
- } else if (Algorithm.Ed448.equals(crv)) {
- bytesLength = 57;
- } else {
- throw new RuntimeException("Invalid JWK representation of OKP type algorithm");
- }
-
- byte[] decodedX = Base64Url.decode(x);
- if (decodedX.length != bytesLength) {
- throw new RuntimeException("Invalid JWK representation of OKP type public key");
- }
-
- // x-coordinate's parity check shown by MSB(bit) of MSB(byte) of decoded "x": 1 is odd, 0 is even
- boolean isOddX = false;
- if ((decodedX[decodedX.length - 1] & -128) != 0) { // 0b10000000
- isOddX = true;
- }
-
- // MSB(bit) of MSB(byte) showing x-coodinate's parity is set to 0
- decodedX[decodedX.length - 1] &= 127; // 0b01111111
-
- // both x and y-coordinate in twisted Edwards curve are always 0 or natural number
- BigInteger y = new BigInteger(1, JWKBuilder.reverseBytes(decodedX));
- NamedParameterSpec spec = new NamedParameterSpec(crv);
- EdECPoint ep = new EdECPoint(isOddX, y);
- EdECPublicKeySpec keySpec = new EdECPublicKeySpec(spec, ep);
-
- PublicKey publicKey = null;
- try {
- publicKey = KeyFactory.getInstance(crv).generatePublic(keySpec);
- } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- return publicKey;
- }
-
- @Override
- public boolean isKeyTypeSupported(String keyType) {
- return (RSAPublicJWK.RSA.equals(keyType) || ECPublicJWK.EC.equals(keyType) || OKPPublicJWK.OKP.equals(keyType));
- }
-
}
diff --git a/core/src/main/java/org/keycloak/sdjwt/IssuerSignedJWT.java b/core/src/main/java/org/keycloak/sdjwt/IssuerSignedJWT.java
index ff31d4d693..250dafad58 100644
--- a/core/src/main/java/org/keycloak/sdjwt/IssuerSignedJWT.java
+++ b/core/src/main/java/org/keycloak/sdjwt/IssuerSignedJWT.java
@@ -16,7 +16,9 @@
*/
package org.keycloak.sdjwt;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -140,7 +142,7 @@ public class IssuerSignedJWT extends SdJws {
* Returns `cnf` claim (establishing key binding)
*/
public Optional getCnfClaim() {
- var cnf = getPayload().get("cnf");
+ JsonNode cnf = getPayload().get("cnf");
return Optional.ofNullable(cnf);
}
@@ -148,7 +150,7 @@ public class IssuerSignedJWT extends SdJws {
* Returns declared hash algorithm from SD hash claim.
*/
public String getSdHashAlg() {
- var hashAlgNode = getPayload().get(CLAIM_NAME_SD_HASH_ALGORITHM);
+ JsonNode hashAlgNode = getPayload().get(CLAIM_NAME_SD_HASH_ALGORITHM);
return hashAlgNode == null ? "sha-256" : hashAlgNode.asText();
}
@@ -159,10 +161,10 @@ public class IssuerSignedJWT extends SdJws {
*/
public void verifySdHashAlgorithm() throws VerificationException {
// Known secure algorithms
- final Set secureAlgorithms = Set.of(
+ final Set secureAlgorithms = new HashSet<>(Arrays.asList(
"sha-256", "sha-384", "sha-512",
"sha3-256", "sha3-384", "sha3-512"
- );
+ ));
// Read SD hash claim
String hashAlg = getSdHashAlg();
diff --git a/core/src/main/java/org/keycloak/sdjwt/SdJwtUtils.java b/core/src/main/java/org/keycloak/sdjwt/SdJwtUtils.java
index a6ef79f9f0..add83f7c52 100644
--- a/core/src/main/java/org/keycloak/sdjwt/SdJwtUtils.java
+++ b/core/src/main/java/org/keycloak/sdjwt/SdJwtUtils.java
@@ -83,7 +83,7 @@ public class SdJwtUtils {
JsonNode jsonNode;
// Decode Base64URL-encoded disclosure
- var decoded = new String(decodeNoPad(disclosure));
+ String decoded = new String(decodeNoPad(disclosure));
// Parse the disclosure string into a JSON array
try {
diff --git a/core/src/main/java/org/keycloak/sdjwt/SdJwtVerificationContext.java b/core/src/main/java/org/keycloak/sdjwt/SdJwtVerificationContext.java
index cce22c6b47..3c55bf4241 100644
--- a/core/src/main/java/org/keycloak/sdjwt/SdJwtVerificationContext.java
+++ b/core/src/main/java/org/keycloak/sdjwt/SdJwtVerificationContext.java
@@ -32,7 +32,9 @@ import org.keycloak.sdjwt.vp.KeyBindingJwtVerificationOpts;
import org.keycloak.util.JWKSUtils;
import java.time.Instant;
+import java.util.AbstractMap;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -75,9 +77,9 @@ public class SdJwtVerificationContext {
private Map computeDigestDisclosureMap(List disclosureStrings) {
return disclosureStrings.stream()
.map(disclosureString -> {
- var digest = SdJwtUtils.hashAndBase64EncodeNoPad(
+ String digest = SdJwtUtils.hashAndBase64EncodeNoPad(
disclosureString.getBytes(), issuerSignedJwt.getSdHashAlg());
- return Map.entry(digest, disclosureString);
+ return new AbstractMap.SimpleEntry(digest, disclosureString);
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
@@ -103,7 +105,7 @@ public class SdJwtVerificationContext {
validateIssuerSignedJwt(issuerSignedJwtVerificationOpts.getVerifier());
// Validate disclosures.
- var disclosedPayload = validateDisclosuresDigests();
+ JsonNode disclosedPayload = validateDisclosuresDigests();
// Validate time claims.
// Issuers will typically include claims controlling the validity of the SD-JWT in plaintext in the
@@ -182,13 +184,13 @@ public class SdJwtVerificationContext {
validateKeyBindingJwtTyp();
// Determine the public key for the Holder from the SD-JWT
- var cnf = issuerSignedJwt.getCnfClaim().orElseThrow(
+ JsonNode cnf = issuerSignedJwt.getCnfClaim().orElseThrow(
() -> new VerificationException("No cnf claim in Issuer-signed JWT for key binding")
);
// Ensure that a signing algorithm was used that was deemed secure for the application.
// The none algorithm MUST NOT be accepted.
- var holderVerifier = buildHolderVerifier(cnf);
+ SignatureVerifierContext holderVerifier = buildHolderVerifier(cnf);
// Validate the signature over the Key Binding JWT
try {
@@ -219,7 +221,7 @@ public class SdJwtVerificationContext {
* @throws VerificationException if verification failed
*/
private void validateKeyBindingJwtTyp() throws VerificationException {
- var typ = keyBindingJwt.getHeader().getType();
+ String typ = keyBindingJwt.getHeader().getType();
if (!typ.equals(KeyBindingJWT.TYP)) {
throw new VerificationException("Key Binding JWT is not of declared typ " + KeyBindingJWT.TYP);
}
@@ -234,7 +236,7 @@ public class SdJwtVerificationContext {
Objects.requireNonNull(cnf);
// Read JWK
- var cnfJwk = cnf.get("jwk");
+ JsonNode cnfJwk = cnf.get("jwk");
if (cnfJwk == null) {
throw new UnsupportedOperationException("Only cnf/jwk claim supported");
}
@@ -394,7 +396,7 @@ public class SdJwtVerificationContext {
Set visitedSalts = new HashSet<>();
Set visitedDigests = new HashSet<>();
Set visitedDisclosureStrings = new HashSet<>();
- var disclosedPayload = validateViaRecursiveDisclosing(
+ JsonNode disclosedPayload = validateViaRecursiveDisclosing(
SdJwtUtils.deepClone(issuerSignedJwt.getPayload()),
visitedSalts, visitedDigests, visitedDisclosureStrings);
@@ -427,11 +429,11 @@ public class SdJwtVerificationContext {
// Find all objects having an _sd key that refers to an array of strings.
if (currentNode.isObject()) {
- var currentObjectNode = ((ObjectNode) currentNode);
+ ObjectNode currentObjectNode = ((ObjectNode) currentNode);
- var sdArray = currentObjectNode.get(IssuerSignedJWT.CLAIM_NAME_SELECTIVE_DISCLOSURE);
+ JsonNode sdArray = currentObjectNode.get(IssuerSignedJWT.CLAIM_NAME_SELECTIVE_DISCLOSURE);
if (sdArray != null && sdArray.isArray()) {
- for (var el : sdArray) {
+ for (JsonNode el : sdArray) {
if (!el.isTextual()) {
throw new VerificationException(
"Unexpected non-string element inside _sd array: " + el
@@ -441,16 +443,16 @@ public class SdJwtVerificationContext {
// Compare the value with the digests calculated previously and find the matching Disclosure.
// If no such Disclosure can be found, the digest MUST be ignored.
- var digest = el.asText();
+ String digest = el.asText();
markDigestAsVisited(digest, visitedDigests);
- var disclosure = disclosures.get(digest);
+ String disclosure = disclosures.get(digest);
if (disclosure != null) {
// Mark disclosure as visited
visitedDisclosureStrings.add(disclosure);
// Validate disclosure format
- var decodedDisclosure = validateSdArrayDigestDisclosureFormat(disclosure);
+ DisclosureFields decodedDisclosure = validateSdArrayDigestDisclosureFormat(disclosure);
// Mark salt as visited
markSaltAsVisited(decodedDisclosure.getSaltValue(), visitedSalts);
@@ -475,29 +477,29 @@ public class SdJwtVerificationContext {
// Find all array elements that are objects with one key, that key being ... and referring to a string
if (currentNode.isArray()) {
- var currentArrayNode = ((ArrayNode) currentNode);
- var indexesToRemove = new ArrayList();
+ ArrayNode currentArrayNode = ((ArrayNode) currentNode);
+ ArrayList indexesToRemove = new ArrayList<>();
for (int i = 0; i < currentArrayNode.size(); ++i) {
- var itemNode = currentArrayNode.get(i);
+ JsonNode itemNode = currentArrayNode.get(i);
if (itemNode.isObject() && itemNode.size() == 1) {
// Check single "..." field
- var field = itemNode.fields().next();
+ Map.Entry field = itemNode.fields().next();
if (field.getKey().equals(UndisclosedArrayElement.SD_CLAIM_NAME)
&& field.getValue().isTextual()) {
// Compare the value with the digests calculated previously and find the matching Disclosure.
// If no such Disclosure can be found, the digest MUST be ignored.
- var digest = field.getValue().asText();
+ String digest = field.getValue().asText();
markDigestAsVisited(digest, visitedDigests);
- var disclosure = disclosures.get(digest);
+ String disclosure = disclosures.get(digest);
if (disclosure != null) {
// Mark disclosure as visited
visitedDisclosureStrings.add(disclosure);
// Validate disclosure format
- var decodedDisclosure = validateArrayElementDigestDisclosureFormat(disclosure);
+ DisclosureFields decodedDisclosure = validateArrayElementDigestDisclosureFormat(disclosure);
// Mark salt as visited
markSaltAsVisited(decodedDisclosure.getSaltValue(), visitedSalts);
@@ -584,10 +586,10 @@ public class SdJwtVerificationContext {
// If the claim name is _sd or ..., the SD-JWT MUST be rejected.
- var denylist = List.of(
+ List denylist = Arrays.asList(new String[]{
IssuerSignedJWT.CLAIM_NAME_SELECTIVE_DISCLOSURE,
UndisclosedArrayElement.SD_CLAIM_NAME
- );
+ });
String claimName = arrayNode.get(1).asText();
if (denylist.contains(claimName)) {
diff --git a/core/src/main/java16/org/keycloak/jose/jwk/JWKBuilder.java b/core/src/main/java16/org/keycloak/jose/jwk/JWKBuilder.java
new file mode 100644
index 0000000000..c3943c742a
--- /dev/null
+++ b/core/src/main/java16/org/keycloak/jose/jwk/JWKBuilder.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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.keycloak.jose.jwk;
+
+import java.math.BigInteger;
+import java.security.Key;
+import java.security.interfaces.EdECPublicKey;
+import java.security.spec.EdECPoint;
+import java.util.Arrays;
+import java.util.Optional;
+
+import org.keycloak.common.util.Base64Url;
+import org.keycloak.common.util.KeyUtils;
+import org.keycloak.crypto.Algorithm;
+import org.keycloak.crypto.KeyType;
+import org.keycloak.crypto.KeyUse;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class JWKBuilder extends AbstractJWKBuilder {
+
+ private JWKBuilder() {
+ }
+
+ public static JWKBuilder create() {
+ return new JWKBuilder();
+ }
+
+ public JWKBuilder kid(String kid) {
+ this.kid = kid;
+ return this;
+ }
+
+ public JWKBuilder algorithm(String algorithm) {
+ this.algorithm = algorithm;
+ return this;
+ }
+
+ @Override
+ public JWK okp(Key key) {
+ return okp(key, DEFAULT_PUBLIC_KEY_USE);
+ }
+
+ @Override
+ public JWK okp(Key key, KeyUse keyUse) {
+ EdECPublicKey eddsaPublicKey = (EdECPublicKey) key;
+
+ OKPPublicJWK k = new OKPPublicJWK();
+
+ String kid = this.kid != null ? this.kid : KeyUtils.createKeyId(key);
+
+ k.setKeyId(kid);
+ k.setKeyType(KeyType.OKP);
+ k.setAlgorithm(algorithm);
+ k.setPublicKeyUse(keyUse == null ? DEFAULT_PUBLIC_KEY_USE.getSpecName() : keyUse.getSpecName());
+ k.setCrv(eddsaPublicKey.getParams().getName());
+
+ Optional x = edPublicKeyInJwkRepresentation(eddsaPublicKey);
+ k.setX(x.orElse(""));
+
+ return k;
+ }
+
+ private Optional edPublicKeyInJwkRepresentation(EdECPublicKey eddsaPublicKey) {
+ EdECPoint edEcPoint = eddsaPublicKey.getPoint();
+ BigInteger yCoordinate = edEcPoint.getY();
+
+ // JWK representation "x" of a public key
+ int bytesLength = 0;
+ if (Algorithm.Ed25519.equals(eddsaPublicKey.getParams().getName())) {
+ bytesLength = 32;
+ } else if (Algorithm.Ed448.equals(eddsaPublicKey.getParams().getName())) {
+ bytesLength = 57;
+ } else {
+ return Optional.ofNullable(null);
+ }
+
+ // consider the case where yCoordinate.toByteArray() is less than bytesLength due to relatively small value of y-coordinate.
+ byte[] yCoordinateLittleEndianBytes = new byte[bytesLength];
+
+ // convert big endian representation of BigInteger to little endian representation of JWK representation (RFC 8032,8027)
+ yCoordinateLittleEndianBytes = Arrays.copyOf(reverseBytes(yCoordinate.toByteArray()), bytesLength);
+
+ // set a parity of x-coordinate to the most significant bit of the last octet (RFC 8032, 8037)
+ if (edEcPoint.isXOdd()) {
+ yCoordinateLittleEndianBytes[yCoordinateLittleEndianBytes.length - 1] |= -128; // 0b10000000
+ }
+
+ return Optional.ofNullable(Base64Url.encode(yCoordinateLittleEndianBytes));
+ }
+
+}
diff --git a/core/src/main/java16/org/keycloak/jose/jwk/JWKParser.java b/core/src/main/java16/org/keycloak/jose/jwk/JWKParser.java
new file mode 100755
index 0000000000..cfa646cbbd
--- /dev/null
+++ b/core/src/main/java16/org/keycloak/jose/jwk/JWKParser.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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.keycloak.jose.jwk;
+
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.EdECPoint;
+import java.security.spec.EdECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.NamedParameterSpec;
+
+import org.keycloak.common.util.Base64Url;
+import org.keycloak.crypto.Algorithm;
+import org.keycloak.crypto.KeyType;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class JWKParser extends AbstractJWKParser {
+
+ private JWKParser() {
+ }
+
+ public static JWKParser create() {
+ return new JWKParser();
+ }
+
+ public JWKParser(JWK jwk) {
+ this.jwk = jwk;
+ }
+
+ public static JWKParser create(JWK jwk) {
+ return new JWKParser(jwk);
+ }
+
+ public JWKParser parse(String jwk) {
+ try {
+ this.jwk = JsonSerialization.mapper.readValue(jwk, JWK.class);
+ return this;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public PublicKey toPublicKey() {
+ if (jwk == null) {
+ throw new IllegalStateException("Not possible to convert to the publicKey. The jwk is not set");
+ }
+ String keyType = jwk.getKeyType();
+ if (KeyType.RSA.equals(keyType)) {
+ return createRSAPublicKey();
+ } else if (KeyType.EC.equals(keyType)) {
+ return createECPublicKey();
+ } else if (KeyType.OKP.equals(keyType)) {
+ return createOKPPublicKey();
+ } else {
+ throw new RuntimeException("Unsupported keyType " + keyType);
+ }
+ }
+
+ private PublicKey createOKPPublicKey() {
+ String x = (String) jwk.getOtherClaims().get(OKPPublicJWK.X);
+ String crv = (String) jwk.getOtherClaims().get(OKPPublicJWK.CRV);
+ // JWK representation "x" of a public key
+ int bytesLength = 0;
+ if (Algorithm.Ed25519.equals(crv)) {
+ bytesLength = 32;
+ } else if (Algorithm.Ed448.equals(crv)) {
+ bytesLength = 57;
+ } else {
+ throw new RuntimeException("Invalid JWK representation of OKP type algorithm");
+ }
+
+ byte[] decodedX = Base64Url.decode(x);
+ if (decodedX.length != bytesLength) {
+ throw new RuntimeException("Invalid JWK representation of OKP type public key");
+ }
+
+ // x-coordinate's parity check shown by MSB(bit) of MSB(byte) of decoded "x": 1 is odd, 0 is even
+ boolean isOddX = false;
+ if ((decodedX[decodedX.length - 1] & -128) != 0) { // 0b10000000
+ isOddX = true;
+ }
+
+ // MSB(bit) of MSB(byte) showing x-coodinate's parity is set to 0
+ decodedX[decodedX.length - 1] &= 127; // 0b01111111
+
+ // both x and y-coordinate in twisted Edwards curve are always 0 or natural number
+ BigInteger y = new BigInteger(1, JWKBuilder.reverseBytes(decodedX));
+ NamedParameterSpec spec = new NamedParameterSpec(crv);
+ EdECPoint ep = new EdECPoint(isOddX, y);
+ EdECPublicKeySpec keySpec = new EdECPublicKeySpec(spec, ep);
+
+ PublicKey publicKey = null;
+ try {
+ publicKey = KeyFactory.getInstance(crv).generatePublic(keySpec);
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ return publicKey;
+ }
+
+ @Override
+ public boolean isKeyTypeSupported(String keyType) {
+ return (RSAPublicJWK.RSA.equals(keyType) || ECPublicJWK.EC.equals(keyType) || OKPPublicJWK.OKP.equals(keyType));
+ }
+
+}
diff --git a/core/src/test/java/org/keycloak/RSAVerifierTest.java b/core/src/test/java/org/keycloak/RSAVerifierTest.java
index f918bab9b5..ce8ee21be2 100755
--- a/core/src/test/java/org/keycloak/RSAVerifierTest.java
+++ b/core/src/test/java/org/keycloak/RSAVerifierTest.java
@@ -38,6 +38,7 @@ import org.keycloak.util.TokenUtil;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.cert.X509Certificate;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -96,7 +97,7 @@ public abstract class RSAVerifierTest {
String encoded = new JWSBuilder()
.jwk(jwk)
- .x5c(List.of(idpCertificate, caCertificate))
+ .x5c(Arrays.asList(new X509Certificate[]{idpCertificate, caCertificate}))
.jsonContent(token)
.rsa256(idpPair.getPrivate());
TokenVerifier tokenVerifier = TokenVerifier.create(encoded, JsonWebToken.class);
diff --git a/core/src/test/java/org/keycloak/sdjwt/SdJwsTest.java b/core/src/test/java/org/keycloak/sdjwt/SdJwsTest.java
index d301a6e252..b26f49167f 100644
--- a/core/src/test/java/org/keycloak/sdjwt/SdJwsTest.java
+++ b/core/src/test/java/org/keycloak/sdjwt/SdJwsTest.java
@@ -26,6 +26,9 @@ import org.keycloak.common.VerificationException;
import org.keycloak.rule.CryptoInitRule;
import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -44,7 +47,7 @@ public abstract class SdJwsTest {
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = mapper.createObjectNode();
node.put("sub", "test");
- node.put("exp", Instant.now().plus(1, TimeUnit.HOURS.toChronoUnit()).getEpochSecond());
+ node.put("exp", Instant.now().plus(1, ChronoUnit.HOURS).getEpochSecond());
node.put("name", "Test User");
return node;
}
@@ -66,7 +69,7 @@ public abstract class SdJwsTest {
@Test
public void testVerifyExpClaim_ExpiredJWT() {
JsonNode payload = createPayload();
- ((ObjectNode) payload).put("exp", Instant.now().minus(1, TimeUnit.HOURS.toChronoUnit()).getEpochSecond());
+ ((ObjectNode) payload).put("exp", Instant.now().minus(1, ChronoUnit.HOURS).getEpochSecond());
SdJws sdJws = new SdJws(payload) {
};
assertThrows(VerificationException.class, sdJws::verifyExpClaim);
@@ -75,7 +78,7 @@ public abstract class SdJwsTest {
@Test
public void testVerifyExpClaim_Positive() throws Exception {
JsonNode payload = createPayload();
- ((ObjectNode) payload).put("exp", Instant.now().plus(1, TimeUnit.HOURS.toChronoUnit()).getEpochSecond());
+ ((ObjectNode) payload).put("exp", Instant.now().plus(1, ChronoUnit.HOURS).getEpochSecond());
SdJws sdJws = new SdJws(payload) {
};
sdJws.verifyExpClaim();
@@ -84,7 +87,7 @@ public abstract class SdJwsTest {
@Test
public void testVerifyNotBeforeClaim_Negative() {
JsonNode payload = createPayload();
- ((ObjectNode) payload).put("nbf", Instant.now().plus(1, TimeUnit.HOURS.toChronoUnit()).getEpochSecond());
+ ((ObjectNode) payload).put("nbf", Instant.now().plus(1, ChronoUnit.HOURS).getEpochSecond());
SdJws sdJws = new SdJws(payload) {
};
assertThrows(VerificationException.class, sdJws::verifyNotBeforeClaim);
@@ -93,7 +96,7 @@ public abstract class SdJwsTest {
@Test
public void testVerifyNotBeforeClaim_Positive() throws Exception {
JsonNode payload = createPayload();
- ((ObjectNode) payload).put("nbf", Instant.now().minus(1, TimeUnit.HOURS.toChronoUnit()).getEpochSecond());
+ ((ObjectNode) payload).put("nbf", Instant.now().minus(1, ChronoUnit.HOURS).getEpochSecond());
SdJws sdJws = new SdJws(payload) {
};
sdJws.verifyNotBeforeClaim();
@@ -124,17 +127,17 @@ public abstract class SdJwsTest {
@Test
public void testVerifyIssClaim_Negative() {
- List allowedIssuers = List.of("issuer1@sdjwt.com", "issuer2@sdjwt.com");
+ List allowedIssuers = Arrays.asList(new String[]{"issuer1@sdjwt.com", "issuer2@sdjwt.com"});
JsonNode payload = createPayload();
((ObjectNode) payload).put("iss", "unknown-issuer@sdjwt.com");
SdJws sdJws = new SdJws(payload) {};
- var exception = assertThrows(VerificationException.class, () -> sdJws.verifyIssClaim(allowedIssuers));
+ VerificationException exception = assertThrows(VerificationException.class, () -> sdJws.verifyIssClaim(allowedIssuers));
assertEquals("Unknown 'iss' claim value: unknown-issuer@sdjwt.com", exception.getMessage());
}
@Test
public void testVerifyIssClaim_Positive() throws VerificationException {
- List allowedIssuers = List.of("issuer1@sdjwt.com", "issuer2@sdjwt.com");
+ List allowedIssuers = Arrays.asList(new String[]{"issuer1@sdjwt.com", "issuer2@sdjwt.com"});
JsonNode payload = createPayload();
((ObjectNode) payload).put("iss", "issuer1@sdjwt.com");
SdJws sdJws = new SdJws(payload) {};
@@ -146,7 +149,7 @@ public abstract class SdJwsTest {
JsonNode payload = createPayload();
((ObjectNode) payload).put("vct", "IdentityCredential");
SdJws sdJws = new SdJws(payload) {};
- var exception = assertThrows(VerificationException.class, () -> sdJws.verifyVctClaim(List.of("PassportCredential")));
+ VerificationException exception = assertThrows(VerificationException.class, () -> sdJws.verifyVctClaim(Collections.singletonList("PassportCredential")));
assertEquals("Unknown 'vct' claim value: IdentityCredential", exception.getMessage());
}
@@ -155,26 +158,26 @@ public abstract class SdJwsTest {
JsonNode payload = createPayload();
((ObjectNode) payload).put("vct", "IdentityCredential");
SdJws sdJws = new SdJws(payload) {};
- sdJws.verifyVctClaim(List.of("IdentityCredential"));
+ sdJws.verifyVctClaim(Collections.singletonList("IdentityCredential"));
}
@Test
public void shouldValidateAgeSinceIssued() throws VerificationException {
long now = Instant.now().getEpochSecond();
- var sdJws = exampleSdJws(now);
+ SdJws sdJws = exampleSdJws(now);
sdJws.verifyAge(180);
}
@Test
public void shouldValidateAgeSinceIssued_IfJwtIsTooOld() {
long now = Instant.now().getEpochSecond();
- var sdJws = exampleSdJws(now - 1000); // that will be too old
- var exception = assertThrows(VerificationException.class, () -> sdJws.verifyAge(180));
+ SdJws sdJws = exampleSdJws(now - 1000); // that will be too old
+ VerificationException exception = assertThrows(VerificationException.class, () -> sdJws.verifyAge(180));
assertEquals("jwt is too old", exception.getMessage());
}
private SdJws exampleSdJws(long iat) {
- var payload = SdJwtUtils.mapper.createObjectNode();
+ ObjectNode payload = SdJwtUtils.mapper.createObjectNode();
payload.set("iat", SdJwtUtils.mapper.valueToTree(iat));
return new SdJws(payload) {
diff --git a/core/src/test/java/org/keycloak/sdjwt/SdJwtVerificationTest.java b/core/src/test/java/org/keycloak/sdjwt/SdJwtVerificationTest.java
index 9a68254651..f6ebc298da 100644
--- a/core/src/test/java/org/keycloak/sdjwt/SdJwtVerificationTest.java
+++ b/core/src/test/java/org/keycloak/sdjwt/SdJwtVerificationTest.java
@@ -26,8 +26,9 @@ import org.keycloak.common.VerificationException;
import org.keycloak.rule.CryptoInitRule;
import java.time.Instant;
-import java.util.List;
-import java.util.Set;
+import java.util.Arrays;
+import java.util.Collections;
+import org.keycloak.crypto.SignatureSignerContext;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.is;
@@ -50,14 +51,14 @@ public abstract class SdJwtVerificationTest {
@Test
public void settingsTest() {
- var issuerSignerContext = testSettings.issuerSigContext;
+ SignatureSignerContext issuerSignerContext = testSettings.issuerSigContext;
assertNotNull(issuerSignerContext);
}
@Test
public void testSdJwtVerification_FlatSdJwt() throws VerificationException {
- for (String hashAlg : List.of("sha-256", "sha-384", "sha-512")) {
- var sdJwt = exampleFlatSdJwtV1()
+ for (String hashAlg : Arrays.asList(new String[]{"sha-256", "sha-384", "sha-512"})) {
+ SdJwt sdJwt = exampleFlatSdJwtV1()
.withHashAlgorithm(hashAlg)
.build();
@@ -67,36 +68,36 @@ public abstract class SdJwtVerificationTest {
@Test
public void testSdJwtVerification_EnforceIdempotence() throws VerificationException {
- var sdJwt = exampleFlatSdJwtV1().build();
+ SdJwt sdJwt = exampleFlatSdJwtV1().build();
sdJwt.verify(defaultIssuerSignedJwtVerificationOpts().build());
sdJwt.verify(defaultIssuerSignedJwtVerificationOpts().build());
}
@Test
public void testSdJwtVerification_SdJwtWithUndisclosedNestedFields() throws VerificationException {
- var sdJwt = exampleSdJwtWithUndisclosedNestedFieldsV1().build();
+ SdJwt sdJwt = exampleSdJwtWithUndisclosedNestedFieldsV1().build();
sdJwt.verify(defaultIssuerSignedJwtVerificationOpts().build());
}
@Test
public void testSdJwtVerification_SdJwtWithUndisclosedArrayElements() throws Exception {
- var sdJwt = exampleSdJwtWithUndisclosedArrayElementsV1().build();
+ SdJwt sdJwt = exampleSdJwtWithUndisclosedArrayElementsV1().build();
sdJwt.verify(defaultIssuerSignedJwtVerificationOpts().build());
}
@Test
public void testSdJwtVerification_RecursiveSdJwt() throws Exception {
- var sdJwt = exampleRecursiveSdJwtV1().build();
+ SdJwt sdJwt = exampleRecursiveSdJwtV1().build();
sdJwt.verify(defaultIssuerSignedJwtVerificationOpts().build());
}
@Test
public void sdJwtVerificationShouldFail_OnInsecureHashAlg() {
- var sdJwt = exampleFlatSdJwtV1()
+ SdJwt sdJwt = exampleFlatSdJwtV1()
.withHashAlgorithm("sha-224") // not deemed secure
.build();
- var exception = assertThrows(
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwt.verify(defaultIssuerSignedJwtVerificationOpts().build())
);
@@ -106,8 +107,8 @@ public abstract class SdJwtVerificationTest {
@Test
public void sdJwtVerificationShouldFail_WithWrongVerifier() {
- var sdJwt = exampleFlatSdJwtV1().build();
- var exception = assertThrows(
+ SdJwt sdJwt = exampleFlatSdJwtV1().build();
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwt.verify(defaultIssuerSignedJwtVerificationOpts()
.withVerifier(testSettings.holderVerifierContext) // wrong verifier
@@ -127,15 +128,15 @@ public abstract class SdJwtVerificationTest {
claimSet.put("exp", now - 1000); // expired 1000 seconds ago
// Exp claim is plain
- var sdJwtV1 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build();
+ SdJwt sdJwtV1 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build();
// Exp claim is undisclosed
- var sdJwtV2 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
- .withRedListedClaimNames(DisclosureRedList.of(Set.of()))
+ SdJwt sdJwtV2 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
+ .withRedListedClaimNames(DisclosureRedList.of(Collections.emptySet()))
.withUndisclosedClaim("exp", "eluV5Og3gSNII8EYnsxA_A")
.build()).build();
- for (SdJwt sdJwt : List.of(sdJwtV1, sdJwtV2)) {
- var exception = assertThrows(
+ for (SdJwt sdJwt : Arrays.asList(new SdJwt[]{sdJwtV1, sdJwtV2})) {
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwt.verify(defaultIssuerSignedJwtVerificationOpts()
.withValidateExpirationClaim(true)
@@ -162,11 +163,11 @@ public abstract class SdJwtVerificationTest {
.withUndisclosedClaim("given_name", "eluV5Og3gSNII8EYnsxA_A")
.build();
- var sdJwtV1 = exampleFlatSdJwtV2(claimSet1, disclosureSpec).build();
- var sdJwtV2 = exampleFlatSdJwtV2(claimSet2, disclosureSpec).build();
+ SdJwt sdJwtV1 = exampleFlatSdJwtV2(claimSet1, disclosureSpec).build();
+ SdJwt sdJwtV2 = exampleFlatSdJwtV2(claimSet2, disclosureSpec).build();
- for (SdJwt sdJwt : List.of(sdJwtV1, sdJwtV2)) {
- var exception = assertThrows(
+ for (SdJwt sdJwt : Arrays.asList(new SdJwt[]{sdJwtV1, sdJwtV2})) {
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwt.verify(defaultIssuerSignedJwtVerificationOpts()
.withValidateExpirationClaim(true)
@@ -187,15 +188,15 @@ public abstract class SdJwtVerificationTest {
claimSet.put("iat", now + 1000); // issued in the future
// Exp claim is plain
- var sdJwtV1 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build();
+ SdJwt sdJwtV1 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build();
// Exp claim is undisclosed
- var sdJwtV2 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
- .withRedListedClaimNames(DisclosureRedList.of(Set.of()))
+ SdJwt sdJwtV2 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
+ .withRedListedClaimNames(DisclosureRedList.of(Collections.emptySet()))
.withUndisclosedClaim("iat", "eluV5Og3gSNII8EYnsxA_A")
.build()).build();
- for (SdJwt sdJwt : List.of(sdJwtV1, sdJwtV2)) {
- var exception = assertThrows(
+ for (SdJwt sdJwt : Arrays.asList(new SdJwt[]{sdJwtV1, sdJwtV2})) {
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwt.verify(defaultIssuerSignedJwtVerificationOpts()
.withValidateIssuedAtClaim(true)
@@ -216,15 +217,15 @@ public abstract class SdJwtVerificationTest {
claimSet.put("nbf", now + 1000); // now will be too soon to accept the jwt
// Exp claim is plain
- var sdJwtV1 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build();
+ SdJwt sdJwtV1 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build();
// Exp claim is undisclosed
- var sdJwtV2 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
- .withRedListedClaimNames(DisclosureRedList.of(Set.of()))
+ SdJwt sdJwtV2 = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
+ .withRedListedClaimNames(DisclosureRedList.of(Collections.emptySet()))
.withUndisclosedClaim("iat", "eluV5Og3gSNII8EYnsxA_A")
.build()).build();
- for (SdJwt sdJwt : List.of(sdJwtV1, sdJwtV2)) {
- var exception = assertThrows(
+ for (SdJwt sdJwt : Arrays.asList(new SdJwt[]{sdJwtV1, sdJwtV2})) {
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwt.verify(defaultIssuerSignedJwtVerificationOpts()
.withValidateNotBeforeClaim(true)
@@ -242,9 +243,9 @@ public abstract class SdJwtVerificationTest {
claimSet.put("given_name", "John");
claimSet.set("_sd", mapper.readTree("[123]"));
- var sdJwt = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build();
+ SdJwt sdJwt = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build();
- var exception = assertThrows(
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwt.verify(defaultIssuerSignedJwtVerificationOpts()
.build())
@@ -255,15 +256,15 @@ public abstract class SdJwtVerificationTest {
@Test
public void sdJwtVerificationShouldFail_IfForbiddenClaimNames() {
- for (String forbiddenClaimName : List.of("_sd", "...")) {
+ for (String forbiddenClaimName : Arrays.asList(new String[]{"_sd", "..."})) {
ObjectNode claimSet = mapper.createObjectNode();
claimSet.put(forbiddenClaimName, "Value");
- var sdJwt = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
+ SdJwt sdJwt = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
.withUndisclosedClaim(forbiddenClaimName, "eluV5Og3gSNII8EYnsxA_A")
.build()).build();
- var exception = assertThrows(
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwt.verify(defaultIssuerSignedJwtVerificationOpts().build())
);
@@ -277,13 +278,13 @@ public abstract class SdJwtVerificationTest {
ObjectNode claimSet = mapper.createObjectNode();
claimSet.put("given_name", "John"); // this same field will also be nested
- var sdJwt = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
+ SdJwt sdJwt = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
.withUndisclosedClaim("given_name", "eluV5Og3gSNII8EYnsxA_A")
.withDecoyClaim("G02NSrQfjFXQ7Io09syajA")
.withDecoyClaim("G02NSrQfjFXQ7Io09syajA")
.build()).build();
- var exception = assertThrows(
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwt.verify(defaultIssuerSignedJwtVerificationOpts().build())
);
@@ -297,14 +298,14 @@ public abstract class SdJwtVerificationTest {
claimSet.put("given_name", "John");
claimSet.put("family_name", "Doe");
- var salt = "eluV5Og3gSNII8EYnsxA_A";
- var sdJwt = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
+ String salt = "eluV5Og3gSNII8EYnsxA_A";
+ SdJwt sdJwt = exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder()
.withUndisclosedClaim("given_name", salt)
// We are reusing the same salt value, and that is the problem
.withUndisclosedClaim("family_name", salt)
.build()).build();
- var exception = assertThrows(
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwt.verify(defaultIssuerSignedJwtVerificationOpts().build())
);
diff --git a/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtVPVerificationTest.java b/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtVPVerificationTest.java
index 3afe4bbf61..d338ce2dbf 100644
--- a/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtVPVerificationTest.java
+++ b/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtVPVerificationTest.java
@@ -33,6 +33,7 @@ import org.keycloak.sdjwt.vp.KeyBindingJwtVerificationOpts;
import org.keycloak.sdjwt.vp.SdJwtVP;
import java.time.Instant;
+import java.util.Arrays;
import java.util.List;
import static org.hamcrest.CoreMatchers.containsString;
@@ -69,9 +70,11 @@ public abstract class SdJwtVPVerificationTest {
@Test
public void testVerif_s20_8_sdjwt_with_kb__AltCnfCurves() throws VerificationException {
- var entries = List.of("sdjwt/s20.8-sdjwt+kb--es384.txt", "sdjwt/s20.8-sdjwt+kb--es512.txt");
+ List entries = Arrays.asList(new String[]{
+ "sdjwt/s20.8-sdjwt+kb--es384.txt", "sdjwt/s20.8-sdjwt+kb--es512.txt"
+ });
- for (var entry : entries) {
+ for (String entry : entries) {
String sdJwtVPString = TestUtils.readFileAsString(getClass(), entry);
SdJwtVP sdJwtVP = SdJwtVP.of(sdJwtVPString);
@@ -84,14 +87,14 @@ public abstract class SdJwtVPVerificationTest {
@Test
public void testVerif_s20_8_sdjwt_with_kb__CnfRSA() throws VerificationException {
- var entries = List.of(
+ List entries = Arrays.asList(new String[]{
"sdjwt/s20.8-sdjwt+kb--cnf-rsa-rs256.txt",
"sdjwt/s20.8-sdjwt+kb--cnf-rsa-ps256.txt",
"sdjwt/s20.8-sdjwt+kb--cnf-rsa-ps384.txt",
"sdjwt/s20.8-sdjwt+kb--cnf-rsa-ps512.txt"
- );
+ });
- for (var entry : entries) {
+ for (String entry : entries) {
String sdJwtVPString = TestUtils.readFileAsString(getClass(), entry);
SdJwtVP sdJwtVP = SdJwtVP.of(sdJwtVPString);
@@ -220,7 +223,7 @@ public abstract class SdJwtVPVerificationTest {
@Test
public void testShouldFail_IfKbSdHashWrongFormat() {
- var kbPayload = exampleKbPayload();
+ ObjectNode kbPayload = exampleKbPayload();
// This hash is not a string
kbPayload.set("sd_hash", mapper.valueToTree(1234));
@@ -235,7 +238,7 @@ public abstract class SdJwtVPVerificationTest {
@Test
public void testShouldFail_IfKbSdHashInvalid() {
- var kbPayload = exampleKbPayload();
+ ObjectNode kbPayload = exampleKbPayload();
// This hash makes no sense
kbPayload.put("sd_hash", "c3FmZHFmZGZlZXNkZmZi");
@@ -252,7 +255,7 @@ public abstract class SdJwtVPVerificationTest {
public void testShouldFail_IfKbIssuedInFuture() {
long now = Instant.now().getEpochSecond();
- var kbPayload = exampleKbPayload();
+ ObjectNode kbPayload = exampleKbPayload();
kbPayload.set("iat", mapper.valueToTree(now + 1000));
testShouldFailGeneric2(
@@ -267,7 +270,7 @@ public abstract class SdJwtVPVerificationTest {
public void testShouldFail_IfKbTooOld() {
long issuerSignedJwtIat = 1683000000; // same value in test vector
- var kbPayload = exampleKbPayload();
+ ObjectNode kbPayload = exampleKbPayload();
// This KB-JWT is then issued more than 60s ago
kbPayload.set("iat", mapper.valueToTree(issuerSignedJwtIat - 120));
@@ -285,7 +288,7 @@ public abstract class SdJwtVPVerificationTest {
public void testShouldFail_IfKbExpired() {
long now = Instant.now().getEpochSecond();
- var kbPayload = exampleKbPayload();
+ ObjectNode kbPayload = exampleKbPayload();
kbPayload.set("exp", mapper.valueToTree(now - 1000));
testShouldFailGeneric2(
@@ -302,7 +305,7 @@ public abstract class SdJwtVPVerificationTest {
public void testShouldFail_IfKbNotBeforeTimeYet() {
long now = Instant.now().getEpochSecond();
- var kbPayload = exampleKbPayload();
+ ObjectNode kbPayload = exampleKbPayload();
kbPayload.set("nbf", mapper.valueToTree(now + 1000));
testShouldFailGeneric2(
@@ -321,7 +324,7 @@ public abstract class SdJwtVPVerificationTest {
String sdJwtVPString = TestUtils.readFileAsString(getClass(), "sdjwt/s20.8-sdjwt+kb--cnf-is-not-jwk.txt");
SdJwtVP sdJwtVP = SdJwtVP.of(sdJwtVPString);
- var exception = assertThrows(
+ UnsupportedOperationException exception = assertThrows(
UnsupportedOperationException.class,
() -> sdJwtVP.verify(
defaultIssuerSignedJwtVerificationOpts().build(),
@@ -363,7 +366,7 @@ public abstract class SdJwtVPVerificationTest {
String sdJwtVPString = TestUtils.readFileAsString(getClass(), testFilePath);
SdJwtVP sdJwtVP = SdJwtVP.of(sdJwtVPString);
- var exception = assertThrows(
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwtVP.verify(
defaultIssuerSignedJwtVerificationOpts().build(),
@@ -399,7 +402,7 @@ public abstract class SdJwtVPVerificationTest {
+ keyBindingJWT.toJws()
);
- var exception = assertThrows(
+ VerificationException exception = assertThrows(
VerificationException.class,
() -> sdJwtVP.verify(
defaultIssuerSignedJwtVerificationOpts().build(),
@@ -431,7 +434,7 @@ public abstract class SdJwtVPVerificationTest {
}
private ObjectNode exampleKbPayload() {
- var payload = mapper.createObjectNode();
+ ObjectNode payload = mapper.createObjectNode();
payload.put("nonce", "1234567890");
payload.put("aud", "https://verifier.example.org");
payload.put("sd_hash", "X9RrrfWt_70gHzOcovGSIt4Fms9Tf2g2hjlWVI_cxZg");
diff --git a/integration/admin-client/pom.xml b/integration/admin-client/pom.xml
index 2d788f1f6e..b818b72628 100755
--- a/integration/admin-client/pom.xml
+++ b/integration/admin-client/pom.xml
@@ -32,6 +32,12 @@
For the use by 3rd party applications, please use `org.keycloak:keycloak-admin-client` module.
+
+ 8
+ 8
+ 8
+
+
org.keycloak