diff --git a/core/src/main/java/org/keycloak/sdjwt/IssuerSignedJWT.java b/core/src/main/java/org/keycloak/sdjwt/IssuerSignedJWT.java index 0e7c36c0e42..dafd8154d08 100644 --- a/core/src/main/java/org/keycloak/sdjwt/IssuerSignedJWT.java +++ b/core/src/main/java/org/keycloak/sdjwt/IssuerSignedJWT.java @@ -70,15 +70,6 @@ public class IssuerSignedJWT extends JwsToken { this.decoyClaims = new ArrayList<>(); } - public IssuerSignedJWT(JWSHeader jwsHeader, - ObjectNode payload, - SignatureSignerContext signer) { - super(jwsHeader, payload, signer); - this.disclosureSpec = null; - this.disclosureClaims = new ArrayList<>(); - this.decoyClaims = new ArrayList<>(); - } - public IssuerSignedJWT(String jwsString) { super(jwsString); this.disclosureSpec = null; @@ -86,71 +77,7 @@ public class IssuerSignedJWT extends JwsToken { this.decoyClaims = new ArrayList<>(); } - public IssuerSignedJWT(DisclosureSpec disclosureSpec, - ObjectNode disclosureClaims) { - this(disclosureSpec, disclosureClaims, OID4VCConstants.SD_HASH_DEFAULT_ALGORITHM); - } - - public IssuerSignedJWT(DisclosureSpec disclosureSpec, - ObjectNode disclosureClaims, - String hashAlg) { - this(disclosureSpec, new JWSHeader(), disclosureClaims, null, hashAlg, false); - } - - public IssuerSignedJWT(DisclosureSpec disclosureSpec, - ObjectNode disclosureClaims, - String hashAlg, - boolean nestedDisclosures) { - this(disclosureSpec, new JWSHeader(), disclosureClaims, null, hashAlg, nestedDisclosures); - } - - public IssuerSignedJWT(DisclosureSpec disclosureSpec, - ObjectNode disclosureClaims, - List decoyClaims, - String hashAlg, - boolean nestedDisclosures) { - this(disclosureSpec, new JWSHeader(), disclosureClaims, decoyClaims, hashAlg, nestedDisclosures); - } - - public IssuerSignedJWT(List disclosureClaims, - List decoyClaims, - String hashAlg, - boolean nestedDisclosures) { - this(DisclosureSpec.builder().build(), new JWSHeader(), - disclosureClaims, decoyClaims, hashAlg, nestedDisclosures); - } - - public IssuerSignedJWT(DisclosureSpec disclosureSpec, - JWSHeader jwsHeader, - ObjectNode disclosureClaims, - List decoyClaims, - String hashAlg, - boolean nestedDisclosures) { - this(disclosureSpec, - jwsHeader, - SdJwtClaimFactory.parsePayload(disclosureClaims, disclosureSpec), - decoyClaims, - hashAlg, - nestedDisclosures); - } - - public IssuerSignedJWT(DisclosureSpec disclosureSpec, - JWSHeader jwsHeader, - ObjectNode disclosureClaims, - List decoyClaims, - String hashAlg, - boolean nestedDisclosures, - SignatureSignerContext signer) { - this(disclosureSpec, - jwsHeader, - SdJwtClaimFactory.parsePayload(disclosureClaims, disclosureSpec), - decoyClaims, - hashAlg, - nestedDisclosures, - signer); - } - - public IssuerSignedJWT(DisclosureSpec disclosureSpec, + protected IssuerSignedJWT(DisclosureSpec disclosureSpec, JWSHeader jwsHeader, List disclosureClaims, List decoyClaims, @@ -162,17 +89,7 @@ public class IssuerSignedJWT extends JwsToken { this.decoyClaims = decoyClaims; } - public IssuerSignedJWT(List disclosureClaims, - List decoyClaims, - String hashAlg, - boolean nestedDisclosures, - SignatureSignerContext signer, - String jwsType) { - this(null, new JWSHeader(null, jwsType, null), - disclosureClaims, decoyClaims, hashAlg, nestedDisclosures, signer); - } - - public IssuerSignedJWT(DisclosureSpec disclosureSpec, + protected IssuerSignedJWT(DisclosureSpec disclosureSpec, JWSHeader jwsHeader, List disclosureClaims, List decoyClaims, diff --git a/core/src/main/java/org/keycloak/sdjwt/JwsToken.java b/core/src/main/java/org/keycloak/sdjwt/JwsToken.java index 77508e6a2f1..4e102fff9ce 100644 --- a/core/src/main/java/org/keycloak/sdjwt/JwsToken.java +++ b/core/src/main/java/org/keycloak/sdjwt/JwsToken.java @@ -49,16 +49,16 @@ public abstract class JwsToken { protected JWSInput jwsInput; - public JwsToken(String jws) { + protected JwsToken(String jws) { parse(jws); } - public JwsToken(JWSHeader jwsHeader, ObjectNode payload) { + protected JwsToken(JWSHeader jwsHeader, ObjectNode payload) { this.jwsHeader = jwsHeader; this.payload = payload; } - public JwsToken(JWSHeader jwsHeader, ObjectNode payload, SignatureSignerContext signerContext) { + protected JwsToken(JWSHeader jwsHeader, ObjectNode payload, SignatureSignerContext signerContext) { this.jwsHeader = jwsHeader; this.payload = payload; this.jws = sign(signerContext); diff --git a/core/src/main/java/org/keycloak/sdjwt/SdJwt.java b/core/src/main/java/org/keycloak/sdjwt/SdJwt.java index 2c7b4d524ee..b5d9cc0e557 100644 --- a/core/src/main/java/org/keycloak/sdjwt/SdJwt.java +++ b/core/src/main/java/org/keycloak/sdjwt/SdJwt.java @@ -208,6 +208,14 @@ public class SdJwt { private KeyBindingJWT keyBindingJWT; + private SignatureSignerContext issuerSigningContext; + + private SignatureSignerContext keyBindingSigningContext; + + private String sdHashAlgorithm; + + private boolean useDefaultDecoys = true; + public Builder withIssuerSignedJwt(IssuerSignedJWT issuerSignedJwt) { this.issuerSignedJwt = issuerSignedJwt; return this; @@ -223,37 +231,27 @@ public class SdJwt { return this; } + public Builder withIssuerSigningContext(SignatureSignerContext issuerSigningContext) { + this.issuerSigningContext = issuerSigningContext; + return this; + } + + public Builder withKeyBindingSigningContext(SignatureSignerContext keyBindingSigningContext) { + this.keyBindingSigningContext = keyBindingSigningContext; + return this; + } + + public Builder withSdHashAlgorithm(String sdHashAlgorithm) { + this.sdHashAlgorithm = sdHashAlgorithm; + return this; + } + + public Builder withUseDefaultDecoys(boolean useDefaultDecoys) { + this.useDefaultDecoys = useDefaultDecoys; + return this; + } + public SdJwt build() { - return build(true); - } - - public SdJwt build(boolean useDefaultDecoys) { - return build(null, null, null, useDefaultDecoys); - } - - public SdJwt build(SignatureSignerContext issuerSigningContext) { - return build(issuerSigningContext, null, null, true); - } - - public SdJwt build(SignatureSignerContext issuerSigningContext, boolean useDefaultDecoys) { - return build(issuerSigningContext, null, null, useDefaultDecoys); - } - - public SdJwt build(SignatureSignerContext issuerSigningContext, - SignatureSignerContext keybindingSigningContext) { - return build(issuerSigningContext, keybindingSigningContext, null, true); - } - - public SdJwt build(SignatureSignerContext issuerSigningContext, - SignatureSignerContext keybindingSigningContext, - boolean useDefaultDecoys) { - return build(issuerSigningContext, keybindingSigningContext, null, useDefaultDecoys); - } - - public SdJwt build(SignatureSignerContext issuerSigningContext, - SignatureSignerContext keybindingSigningContext, - String sdHashAlgorithm, - boolean useDefaultDecoys) { int numberOfDecoys = Optional.ofNullable(issuerSignedJwt.getDecoyClaims()).map(List::size).orElse(0); if (useDefaultDecoys && numberOfDecoys == 0) { List decoyClaims = new ArrayList<>(); @@ -288,7 +286,7 @@ public class SdJwt { } String sdHash = SdJwtUtils.hashAndBase64EncodeNoPad(sdHashString.getBytes(), hashAlgorithm); keyBindJwt.getPayload().put(OID4VCConstants.SD_HASH, sdHash); - Optional.ofNullable(keybindingSigningContext).ifPresent(keyBindJwt::sign); + Optional.ofNullable(keyBindingSigningContext).ifPresent(keyBindJwt::sign); }); // if issuerSignedJwt was not signed yet if (issuerSigningContext != null && signCounter.get() == 0) { diff --git a/core/src/main/java/org/keycloak/sdjwt/vp/KeyBindingJWT.java b/core/src/main/java/org/keycloak/sdjwt/vp/KeyBindingJWT.java index 136908185de..99b73c8fbbe 100644 --- a/core/src/main/java/org/keycloak/sdjwt/vp/KeyBindingJWT.java +++ b/core/src/main/java/org/keycloak/sdjwt/vp/KeyBindingJWT.java @@ -40,19 +40,7 @@ public class KeyBindingJWT extends JwsToken { super(jwsString); } - public KeyBindingJWT(ObjectNode payload, SignatureSignerContext signer) { - this(new JWSHeader(), payload, signer); - } - - public KeyBindingJWT(ObjectNode payload) { - this(new JWSHeader(), payload, null); - } - - public KeyBindingJWT(JWSHeader jwsHeader, ObjectNode payload) { - this(jwsHeader, payload, null); - } - - public KeyBindingJWT(JWSHeader jwsHeader, ObjectNode payload, SignatureSignerContext signer) { + protected KeyBindingJWT(JWSHeader jwsHeader, ObjectNode payload, SignatureSignerContext signer) { super(jwsHeader, payload); getJwsHeader().setType(OID4VCConstants.KEYBINDING_JWT_TYP); Optional.ofNullable(signer).ifPresent(this::sign); @@ -68,6 +56,8 @@ public class KeyBindingJWT extends JwsToken { protected ObjectNode payload; + private SignatureSignerContext signerContext; + public Builder() { this.jwsHeader = new JWSHeader(); this.payload = JsonNodeFactory.instance.objectNode(); @@ -151,12 +141,13 @@ public class KeyBindingJWT extends JwsToken { return this; } - public KeyBindingJWT build() { - return new KeyBindingJWT(jwsHeader, payload); + public Builder withSignerContext(SignatureSignerContext signatureSignerContext) { + this.signerContext = signatureSignerContext; + return this; } - public KeyBindingJWT build(SignatureSignerContext signer) { - return new KeyBindingJWT(jwsHeader, payload, signer); + public KeyBindingJWT build() { + return new KeyBindingJWT(jwsHeader, payload, signerContext); } } } diff --git a/core/src/main/java/org/keycloak/sdjwt/vp/SdJwtVP.java b/core/src/main/java/org/keycloak/sdjwt/vp/SdJwtVP.java index f8182e83672..fb1cfe3b532 100644 --- a/core/src/main/java/org/keycloak/sdjwt/vp/SdJwtVP.java +++ b/core/src/main/java/org/keycloak/sdjwt/vp/SdJwtVP.java @@ -230,7 +230,10 @@ public class SdJwtVP { } String sd_hash = SdJwtUtils.hashAndBase64EncodeNoPad(unboundPresentation.getBytes(), getHashAlgorithm()); keyBindingClaims.put(SD_HASH, sd_hash); - KeyBindingJWT keyBindingJWT = new KeyBindingJWT(keyBindingClaims, holdSignatureSignerContext); + KeyBindingJWT keyBindingJWT = KeyBindingJWT.builder() + .withPayload(keyBindingClaims) + .withSignerContext(holdSignatureSignerContext) + .build(); sb.append(keyBindingJWT.getJws()); return sb.toString(); } diff --git a/core/src/test/java/org/keycloak/sdjwt/ArrayElementDisclosureTest.java b/core/src/test/java/org/keycloak/sdjwt/ArrayElementDisclosureTest.java index 8c257d563cb..25c851639d0 100644 --- a/core/src/test/java/org/keycloak/sdjwt/ArrayElementDisclosureTest.java +++ b/core/src/test/java/org/keycloak/sdjwt/ArrayElementDisclosureTest.java @@ -39,10 +39,13 @@ public class ArrayElementDisclosureTest { .withUndisclosedArrayElt("nationalities", 1, "nPuoQnkRFq3BIeAm7AnXFA") .build(); - IssuerSignedJWT issuerSignedJWT = new IssuerSignedJWT(disclosureSpec, claimSet); + IssuerSignedJWT issuerSignedJWT = IssuerSignedJWT.builder() + .withClaims(claimSet, disclosureSpec) + .build(); SdJwt sdJwt = SdJwt.builder() .withIssuerSignedJwt(issuerSignedJWT) - .build(false); + .withUseDefaultDecoys(false) + .build(); IssuerSignedJWT jwt = sdJwt.getIssuerSignedJWT(); @@ -65,10 +68,13 @@ public class ArrayElementDisclosureTest { .withDecoyArrayElt("nationalities", 1, "5bPs1IquZNa0hkaFzzzZNw") .build(); - IssuerSignedJWT issuerSignedJWT = new IssuerSignedJWT(disclosureSpec, claimSet); + IssuerSignedJWT issuerSignedJWT = IssuerSignedJWT.builder() + .withClaims(claimSet, disclosureSpec) + .build(); SdJwt sdJwt = SdJwt.builder() .withIssuerSignedJwt(issuerSignedJWT) - .build(false); + .withUseDefaultDecoys(false) + .build(); IssuerSignedJWT jwt = sdJwt.getIssuerSignedJWT(); JsonNode expected = TestUtils.readClaimSet(getClass(), diff --git a/core/src/test/java/org/keycloak/sdjwt/IssuerSignedJWTTest.java b/core/src/test/java/org/keycloak/sdjwt/IssuerSignedJWTTest.java index 8e8c9a3f69a..2e25955e17c 100644 --- a/core/src/test/java/org/keycloak/sdjwt/IssuerSignedJWTTest.java +++ b/core/src/test/java/org/keycloak/sdjwt/IssuerSignedJWTTest.java @@ -91,7 +91,8 @@ public class IssuerSignedJWTTest { SdJwt sdJwt = SdJwt.builder() .withIssuerSignedJwt(IssuerSignedJWT.builder().withClaims(claimSet, disclosureSpec).build()) - .build(false); + .withUseDefaultDecoys(false) + .build(); IssuerSignedJWT jwt = sdJwt.getIssuerSignedJWT(); @@ -123,7 +124,8 @@ public class IssuerSignedJWTTest { SdJwt sdJwt = SdJwt.builder() .withIssuerSignedJwt(IssuerSignedJWT.builder().withClaims(holderClaimSet, disclosureSpec).build()) - .build(false); + .withUseDefaultDecoys(false) + .build(); IssuerSignedJWT jwt = sdJwt.getIssuerSignedJWT(); JsonNode expected = TestUtils.readClaimSet(getClass(), "sdjwt/s3.3-issuer-payload.json"); diff --git a/core/src/test/java/org/keycloak/sdjwt/SdJWTSamplesTest.java b/core/src/test/java/org/keycloak/sdjwt/SdJWTSamplesTest.java index 0159c77e9df..a2369dba218 100644 --- a/core/src/test/java/org/keycloak/sdjwt/SdJWTSamplesTest.java +++ b/core/src/test/java/org/keycloak/sdjwt/SdJWTSamplesTest.java @@ -42,7 +42,8 @@ public class SdJWTSamplesTest { .build(); SdJwt sdJwt = SdJwt.builder() .withIssuerSignedJwt(IssuerSignedJWT.builder().withClaims(holderClaimSet, disclosureSpec).build()) - .build(false); + .withUseDefaultDecoys(false) + .build(); IssuerSignedJWT jwt = sdJwt.getIssuerSignedJWT(); ObjectNode expected = TestUtils.readClaimSet(getClass(), "sdjwt/s7.1-issuer-payload.json"); @@ -70,7 +71,8 @@ public class SdJWTSamplesTest { // produce the nested sdJwt SdJwt addrSdJWT = SdJwt.builder() .withIssuerSignedJwt(IssuerSignedJWT.builder().withClaims(addressClaimSet, addrDisclosureSpec).build()) - .build(false); + .withUseDefaultDecoys(false) + .build(); // cleanup e.g nested _sd_alg JsonNode addPayload = addrSdJWT.asNestedPayload(); // Set payload back into main claim set @@ -84,7 +86,8 @@ public class SdJWTSamplesTest { .withHashAlg(OID4VCConstants.SD_HASH_DEFAULT_ALGORITHM) .build()) .withNestedSdJwt(addrSdJWT) - .build(false); + .withUseDefaultDecoys(false) + .build(); IssuerSignedJWT jwt = sdJwt.getIssuerSignedJWT(); ObjectNode expected = TestUtils.readClaimSet(getClass(), "sdjwt/s7.2-issuer-payload.json"); @@ -111,7 +114,8 @@ public class SdJWTSamplesTest { // produce the nested sdJwt SdJwt addrSdJWT = SdJwt.builder() .withIssuerSignedJwt(IssuerSignedJWT.builder().withClaims(addressClaimSet, addrDisclosureSpec).build()) - .build(false); + .withUseDefaultDecoys(false) + .build(); // cleanup e.g nested _sd_alg JsonNode addPayload = addrSdJWT.asNestedPayload(); // Set payload back into main claim set @@ -122,7 +126,8 @@ public class SdJWTSamplesTest { SdJwt sdJwt = SdJwt.builder() .withIssuerSignedJwt(IssuerSignedJWT.builder().withClaims(holderClaimSet, disclosureSpec).build()) .withNestedSdJwt(addrSdJWT) - .build(false); + .withUseDefaultDecoys(false) + .build(); IssuerSignedJWT jwt = sdJwt.getIssuerSignedJWT(); ObjectNode expected = TestUtils.readClaimSet(getClass(), "sdjwt/s7.2b-issuer-payload.json"); @@ -150,7 +155,8 @@ public class SdJWTSamplesTest { // produce the nested sdJwt SdJwt addrSdJWT = SdJwt.builder() .withIssuerSignedJwt(IssuerSignedJWT.builder().withClaims(addressClaimSet, addrDisclosureSpec).build()) - .build(false); + .withUseDefaultDecoys(false) + .build(); // cleanup e.g nested _sd_alg JsonNode addPayload = addrSdJWT.asNestedPayload(); // Set payload back into main claim set @@ -162,7 +168,8 @@ public class SdJWTSamplesTest { // produce the main sdJwt, adding nested sdJwts SdJwt sdJwt = SdJwt.builder() .withIssuerSignedJwt(IssuerSignedJWT.builder().withClaims(holderClaimSet, disclosureSpec).build()) - .build(false); + .withUseDefaultDecoys(false) + .build(); IssuerSignedJWT jwt = sdJwt.getIssuerSignedJWT(); JsonNode expected = TestUtils.readClaimSet(getClass(), "sdjwt/s7.3-issuer-payload.json"); diff --git a/core/src/test/java/org/keycloak/sdjwt/SdJwtCreationAndSigningTest.java b/core/src/test/java/org/keycloak/sdjwt/SdJwtCreationAndSigningTest.java index d2aa794f8a3..4b4c9a6c146 100644 --- a/core/src/test/java/org/keycloak/sdjwt/SdJwtCreationAndSigningTest.java +++ b/core/src/test/java/org/keycloak/sdjwt/SdJwtCreationAndSigningTest.java @@ -231,9 +231,11 @@ public abstract class SdJwtCreationAndSigningTest { .build(); SdJwt sdJwt = SdJwt.builder() - .withIssuerSignedJwt(issuerSignedJWT) - .withKeybindingJwt(keyBindingJWT) - .build(issuerSignerContext, holderSignerContext); + .withIssuerSignedJwt(issuerSignedJWT) + .withKeybindingJwt(keyBindingJWT) + .withIssuerSigningContext(issuerSignerContext) + .withKeyBindingSigningContext(holderSignerContext) + .build(); // validate object content { diff --git a/core/src/test/java/org/keycloak/sdjwt/SdJwtVerificationTest.java b/core/src/test/java/org/keycloak/sdjwt/SdJwtVerificationTest.java index acf5af9a387..458e2421830 100644 --- a/core/src/test/java/org/keycloak/sdjwt/SdJwtVerificationTest.java +++ b/core/src/test/java/org/keycloak/sdjwt/SdJwtVerificationTest.java @@ -69,7 +69,8 @@ public abstract class SdJwtVerificationTest { IssuerSignedJWT issuerSignedJWT = exampleFlatSdJwtV1().withHashAlg(hashAlg).build(); SdJwt sdJwt = SdJwt.builder() .withIssuerSignedJwt(issuerSignedJWT) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); sdJwt.verify( defaultIssuerVerifyingKeys(), @@ -81,7 +82,9 @@ public abstract class SdJwtVerificationTest { @Test public void testSdJwtVerification_EnforceIdempotence() throws VerificationException { IssuerSignedJWT issuerSignedJWT = exampleFlatSdJwtV1().build(); - SdJwt sdJwt = SdJwt.builder().withIssuerSignedJwt(issuerSignedJWT).build(testSettings.issuerSigContext); + SdJwt sdJwt = SdJwt.builder().withIssuerSignedJwt(issuerSignedJWT) + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); sdJwt.verify( defaultIssuerVerifyingKeys(), @@ -97,7 +100,8 @@ public abstract class SdJwtVerificationTest { @Test public void testSdJwtVerification_SdJwtWithUndisclosedNestedFields() throws VerificationException { SdJwt sdJwt = SdJwt.builder().withIssuerSignedJwt(exampleSdJwtWithUndisclosedNestedFieldsV1().build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); sdJwt.verify( defaultIssuerVerifyingKeys(), @@ -117,7 +121,9 @@ public abstract class SdJwtVerificationTest { @Test public void testSdJwtVerification_RecursiveSdJwt() throws Exception { - SdJwt sdJwt = exampleRecursiveSdJwtV1().build(testSettings.issuerSigContext); + SdJwt sdJwt = exampleRecursiveSdJwtV1() + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); sdJwt.verify( defaultIssuerVerifyingKeys(), @@ -129,8 +135,9 @@ public abstract class SdJwtVerificationTest { public void sdJwtVerificationShouldFail_OnInsecureHashAlg() { IssuerSignedJWT issuerSignedJWT = exampleFlatSdJwtV1().withHashAlg("sha-224").build(); SdJwt sdJwt = SdJwt.builder() - .withIssuerSignedJwt(issuerSignedJWT) // not deemed secure - .build(testSettings.issuerSigContext); + .withIssuerSignedJwt(issuerSignedJWT) // not deemed secure + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); VerificationException exception = assertThrows( VerificationException.class, @@ -146,7 +153,9 @@ public abstract class SdJwtVerificationTest { @Test public void sdJwtVerificationShouldFail_WithWrongVerifier() { IssuerSignedJWT issuerSignedJWT = exampleFlatSdJwtV1().build(); - SdJwt sdJwt = SdJwt.builder().withIssuerSignedJwt(issuerSignedJWT).build(testSettings.issuerSigContext); + SdJwt sdJwt = SdJwt.builder().withIssuerSignedJwt(issuerSignedJWT) + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); VerificationException exception = assertThrows( VerificationException.class, () -> sdJwt.verify( @@ -168,8 +177,9 @@ public abstract class SdJwtVerificationTest { // Exp claim is plain SdJwt sdJwtV1 = SdJwt.builder() - .withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build()) - .build(testSettings.issuerSigContext); + .withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build()) + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); // Exp claim is undisclosed SdJwt sdJwtV2 = SdJwt.builder() .withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet, @@ -177,7 +187,8 @@ public abstract class SdJwtVerificationTest { .withRedListedClaimNames(DisclosureRedList.of(Collections.emptySet())) .withUndisclosedClaim("exp", "eluV5Og3gSNII8EYnsxA_A") .build()).build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); Function verify = sdJwt -> { return assertThrows(VerificationException.class, @@ -238,7 +249,8 @@ public abstract class SdJwtVerificationTest { { SdJwt sdJwtV1 = SdJwt.builder() .withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet1, disclosureSpec).build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); VerificationException exception = verify.apply(sdJwtV1); assertTrue(String.format("Unexpected error message:\n\tMessage was: %s", exception.getMessage()), exception.getMessage().matches("Token has expired by exp: now: '\\d+', exp: '\\d+'")); @@ -246,7 +258,8 @@ public abstract class SdJwtVerificationTest { { SdJwt sdJwtV2 = SdJwt.builder() .withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet2, disclosureSpec).build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); VerificationException exception = verify.apply(sdJwtV2); assertEquals(String.format("Unexpected error message:\n\tMessage was: %s", exception.getMessage()), "Missing required claim 'exp'", exception.getMessage()); @@ -265,7 +278,8 @@ public abstract class SdJwtVerificationTest { SdJwt sdJwtV1 = SdJwt.builder() .withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); // Exp claim is undisclosed SdJwt sdJwtV2 = SdJwt.builder() .withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet, @@ -273,7 +287,8 @@ public abstract class SdJwtVerificationTest { .withRedListedClaimNames(DisclosureRedList.of(Collections.emptySet())) .withUndisclosedClaim("iat", "eluV5Og3gSNII8EYnsxA_A") .build()).build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); Function verify = sdJwt -> { return assertThrows(VerificationException.class, @@ -311,7 +326,8 @@ public abstract class SdJwtVerificationTest { // Exp claim is plain SdJwt sdJwtV1 = SdJwt.builder() .withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()).build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); // Exp claim is undisclosed SdJwt sdJwtV2 = SdJwt.builder() .withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet, @@ -319,7 +335,8 @@ public abstract class SdJwtVerificationTest { .withRedListedClaimNames(DisclosureRedList.of(Collections.emptySet())) .withUndisclosedClaim("iat", "eluV5Og3gSNII8EYnsxA_A") .build()).build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); for (SdJwt sdJwt : Arrays.asList(sdJwtV1, sdJwtV2)) { VerificationException exception = assertThrows( @@ -343,7 +360,8 @@ public abstract class SdJwtVerificationTest { SdJwt sdJwt = SdJwt.builder().withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet, DisclosureSpec.builder().build()) .build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); VerificationException exception = assertThrows( VerificationException.class, @@ -367,7 +385,8 @@ public abstract class SdJwtVerificationTest { DisclosureSpec.builder() .withUndisclosedClaim(forbiddenClaimName, "eluV5Og3gSNII8EYnsxA_A") .build()).build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); VerificationException exception = assertThrows( VerificationException.class, @@ -393,7 +412,8 @@ public abstract class SdJwtVerificationTest { .withDecoyClaim("G02NSrQfjFXQ7Io09syajA") .withDecoyClaim("G02NSrQfjFXQ7Io09syajA") .build()).build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); VerificationException exception = assertThrows( VerificationException.class, @@ -423,7 +443,8 @@ public abstract class SdJwtVerificationTest { .build(); SdJwt.builder() .withIssuerSignedJwt(exampleFlatSdJwtV2(claimSet, disclosureSpec).build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); } ); @@ -530,7 +551,8 @@ public abstract class SdJwtVerificationTest { .withIssuerSignedJwt(IssuerSignedJWT.builder() .withClaims(claimSet, disclosureSpec) .build()) - .build(testSettings.issuerSigContext); + .withIssuerSigningContext(testSettings.issuerSigContext) + .build(); } private SdJwt.Builder exampleRecursiveSdJwtV1() { diff --git a/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtKeyBindingTest.java b/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtKeyBindingTest.java index d4ece01b1f7..abde5b0028a 100644 --- a/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtKeyBindingTest.java +++ b/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtKeyBindingTest.java @@ -140,7 +140,9 @@ public abstract class SdJwtKeyBindingTest { SdJwt sdJwt = SdJwt.builder() .withIssuerSignedJwt(issuerSignedJWT) .withKeybindingJwt(keyBindingJWT) - .build(TestSettings.getInstance().getIssuerSignerContext(), KeyWrapperUtil.createSignatureSignerContext(keyWrapper)); + .withIssuerSigningContext(TestSettings.getInstance().getIssuerSignerContext()) + .withKeyBindingSigningContext(KeyWrapperUtil.createSignatureSignerContext(keyWrapper)) + .build(); String sdJwtString = sdJwt.toString(); @@ -168,7 +170,9 @@ public abstract class SdJwtKeyBindingTest { String invalidSdJwt = SdJwt.builder() .withIssuerSignedJwt(issuerSignedJWT) .withKeybindingJwt(invalidBindingJWT) - .build(TestSettings.getInstance().getIssuerSignerContext(), KeyWrapperUtil.createSignatureSignerContext(keyWrapper)) + .withIssuerSigningContext(TestSettings.getInstance().getIssuerSignerContext()) + .withKeyBindingSigningContext(KeyWrapperUtil.createSignatureSignerContext(keyWrapper)) + .build() .toString(); // Replace signature with the signature from valid sdJwt diff --git a/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtVPTest.java b/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtVPTest.java index 3b67a577dbe..fa2a597e3b8 100644 --- a/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtVPTest.java +++ b/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtVPTest.java @@ -72,7 +72,9 @@ public abstract class SdJwtVPTest { IssuerSignedJWT issuerSignedJWT = IssuerSignedJWT.builder().withClaims(holderClaimSet, disclosureSpec).build(); SdJwt sdJwt = SdJwt.builder() .withIssuerSignedJwt(issuerSignedJWT) - .build(TestSettings.getInstance().getIssuerSignerContext(), false); + .withIssuerSigningContext(TestSettings.getInstance().getIssuerSignerContext()) + .withUseDefaultDecoys(false) + .build(); IssuerSignedJWT jwt = sdJwt.getIssuerSignedJWT(); 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 d88576fa083..d062df1d40f 100644 --- a/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtVPVerificationTest.java +++ b/core/src/test/java/org/keycloak/sdjwt/sdjwtvp/SdJwtVPVerificationTest.java @@ -512,7 +512,10 @@ public abstract class SdJwtVPVerificationTest { } private SdJwtVP exampleSdJwtWithCustomKbPayload(ObjectNode kbPayloadSubstitute) { - KeyBindingJWT keyBindingJWT = new KeyBindingJWT(kbPayloadSubstitute, testSettings.holderSigContext); + KeyBindingJWT keyBindingJWT = KeyBindingJWT.builder() + .withPayload(kbPayloadSubstitute) + .withSignerContext(testSettings.holderSigContext) + .build(); String sdJwtVPString = TestUtils.readFileAsString(getClass(), "sdjwt/s20.1-sdjwt+kb.txt"); String sdJwtWithoutKb = sdJwtVPString.substring(0, sdJwtVPString.lastIndexOf(OID4VCConstants.SDJWT_DELIMITER) + 1); diff --git a/services/src/main/java/org/keycloak/protocol/oid4vc/issuance/credentialbuilder/SdJwtCredentialBody.java b/services/src/main/java/org/keycloak/protocol/oid4vc/issuance/credentialbuilder/SdJwtCredentialBody.java index 067387a94cc..917c60a2625 100644 --- a/services/src/main/java/org/keycloak/protocol/oid4vc/issuance/credentialbuilder/SdJwtCredentialBody.java +++ b/services/src/main/java/org/keycloak/protocol/oid4vc/issuance/credentialbuilder/SdJwtCredentialBody.java @@ -54,7 +54,8 @@ public class SdJwtCredentialBody implements CredentialBody { public String sign(SignatureSignerContext signatureSignerContext) { SdJwt sdJwt = sdJwtBuilder.withIssuerSignedJwt(issuerSignedJWT) - .build(signatureSignerContext); + .withIssuerSigningContext(signatureSignerContext) + .build(); return sdJwt.toSdJwtString(); }