Skip to content

NimbusJwtEncoder should simplify constructing with javax.security Keys #17033

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

surajbh123
Copy link

No description provided.

@surajbh123
Copy link
Author

surajbh123 commented May 3, 2025

Working on test cases and code refactoring

@surajbh123 surajbh123 force-pushed the nimbus-ecoder-builder branch 2 times, most recently from 78ef577 to 6370999 Compare May 4, 2025 18:43
@surajbh123
Copy link
Author

Helper Methods for Key Generation

        // 1. Using SecretKey with defaults
        SecretKey defaultSecretKey = createSecretKey("HmacSHA256", 256); // Key for default HS256
        NimbusJwtEncoder encoderWithSecretDefaults = NimbusJwtEncoder.withSecretKey(defaultSecretKey)
                // .macAlgorithm(MacAlgorithm.HS256) // Default is HS256
                // .keyId(null)                     // Default keyId is null
                // .jwkSelector(...)               // Default selector throws if multiple keys match
                .build();
                
        // 2. Using SecretKey with specific algorithm and keyId
        SecretKey hs512SecretKey = createSecretKey("HmacSHA512", 512);
        String secretKeyId = "my-hmac-key-01";
        NimbusJwtEncoder encoderWithSecretCustom = NimbusJwtEncoder.withSecretKey(hs512SecretKey)
                .macAlgorithm(MacAlgorithm.HS512) // Specify HS512
                .keyId(secretKeyId)               // Specify a key ID
                .build();

        // 3. Using RSA KeyPair with defaults
        KeyPair rsaKeyPair = createRsaKeyPair(2048);
        NimbusJwtEncoder encoderWithRsaDefaults = NimbusJwtEncoder.withKeyPair(rsaKeyPair)
                // .signatureAlgorithm(SignatureAlgorithm.RS256) // Default for RSA is RS256
                // .keyId(UUID generated)                      // Default keyId is a random UUID
                // .jwkSelector(...)                          // Default selector picks first if multiple
                .build();
                
        // 4. Using RSA KeyPair with specific algorithm (PSS) and keyId
        String rsaKeyId = "my-rsa-key-01";
        NimbusJwtEncoder encoderWithRsaCustom = NimbusJwtEncoder.withKeyPair(rsaKeyPair)
                .signatureAlgorithm(SignatureAlgorithm.PS256) // Specify PS256
                .keyId(rsaKeyId)                              // Specify a key ID
                .build();

        // 5. Using EC KeyPair with defaults
        KeyPair ecP256KeyPair = createEcKeyPair(Curve.P_256); // Key for default ES256
        NimbusJwtEncoder encoderWithEcDefaults = NimbusJwtEncoder.withKeyPair(ecP256KeyPair)
                // .signatureAlgorithm(SignatureAlgorithm.ES256) // Default for EC is ES256
                // .keyId(UUID generated)                      // Default keyId is a random UUID
                // .jwkSelector(...)                          // Default selector picks first if multiple
                .build();

        // 6. Using EC KeyPair with specific algorithm and keyId
        KeyPair ecP521KeyPair = createEcKeyPair(Curve.P_521); // Key for ES512
        String ecKeyId = "my-ec-key-01";
        NimbusJwtEncoder encoderWithEcCustom = NimbusJwtEncoder.withKeyPair(ecP521KeyPair)
                .signatureAlgorithm(SignatureAlgorithm.ES512) // Specify ES512
                .keyId(ecKeyId)                               // Specify a key ID
                .build();

@jzheaux jzheaux added this to the 7.0.x milestone May 6, 2025
@jzheaux jzheaux added type: enhancement A general enhancement in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) and removed status: waiting-for-triage An issue we've not yet triaged labels May 6, 2025
Copy link
Contributor

@jzheaux jzheaux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @surajbh123! This PR will make using NimbusJwtEncoder much simpler.

I've left my feedback inline. In addition to that, please ensure that your commit only has the signoff in the description and not in the title. Please also ensure that the description has the issue that this PR resolves:

Add NimbusJwtEncoder Builders

Closes gh-16267

Signed-off-by ...

*/
public KeyPairJwtEncoderBuilder signatureAlgorithm(SignatureAlgorithm signatureAlgorithm) {
Assert.notNull(signatureAlgorithm, "signatureAlgorithm cannot be null");
this.jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's please validate the signature algorithm at this point instead.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can,t do now as as now we have two separate builder for RSA and EC

Copy link
Contributor

@jzheaux jzheaux May 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please push this down to the subclass, have the subclass provide default and valid algorithms by calling the super constructor, or do not use the abstract class so that you can validate here. From a security perspective, it's valuable to validate as early as possible.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@jzheaux
Copy link
Contributor

jzheaux commented May 7, 2025

Just one more thing, @surajbh123, when you submit, it will save you time in the review process if you run the following before submitting:

./gradlew format && ./gradlew :spring-security-oauth2-jose:check

@jzheaux jzheaux self-assigned this May 7, 2025
@surajbh123 surajbh123 force-pushed the nimbus-ecoder-builder branch from 6370999 to 567e729 Compare May 8, 2025 20:14
@surajbh123
Copy link
Author

surajbh123 commented May 8, 2025

Working on reviews comments
Not all resolved yet

@surajbh123 surajbh123 force-pushed the nimbus-ecoder-builder branch 3 times, most recently from bb6d143 to d6154cb Compare May 9, 2025 15:45
Closes spring-projectsgh-16267

Signed-off-by: Suraj Bhadrike <[email protected]>
@surajbh123 surajbh123 force-pushed the nimbus-ecoder-builder branch from d6154cb to 52ef97d Compare May 9, 2025 15:52
Copy link
Contributor

@jzheaux jzheaux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick turnaround, @surajbh123! This is coming together nicely. I've added some additional inline feedback.

@@ -119,14 +133,16 @@ public void setJwkSelector(Converter<List<JWK>, JWK> jwkSelector) {
this.jwkSelector = jwkSelector;
}

public void setJwsHeader(JwsHeader jwsHeader) {
this.jwsHeader = jwsHeader;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check for null here instead of in encode. You can use Assert.notNull for this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it can never be null here
as we setting default value if jws header is null i.e. not sent by user

* with a {@link SecretKey}.
*/
public NimbusJwtEncoder build() {
this.jwsAlgorithm = (this.jwsAlgorithm != null) ? this.jwsAlgorithm : JWSAlgorithm.HS256;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are giving this a default value and there isn't a way for the application to set it back to null, I believe you can remove this line.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the following case, it may be null.

    SecretKey defaultSecretKey = createSecretKey("HmacSHA256", 256); // Key for default HS256
    NimbusJwtEncoder encoderWithSecretDefaults = NimbusJwtEncoder.withSecretKey(defaultSecretKey)
            // .macAlgorithm(MacAlgorithm.HS256) // Default is HS256
            // .keyId(null)                     // Default keyId is null
            // .jwkSelector(...)               // Default selector throws if multiple keys match
            .build();

*/
public KeyPairJwtEncoderBuilder signatureAlgorithm(SignatureAlgorithm signatureAlgorithm) {
Assert.notNull(signatureAlgorithm, "signatureAlgorithm cannot be null");
this.jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());
Copy link
Contributor

@jzheaux jzheaux May 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please push this down to the subclass, have the subclass provide default and valid algorithms by calling the super constructor, or do not use the abstract class so that you can validate here. From a security perspective, it's valuable to validate as early as possible.

*
* @since 7.0
*/
public abstract static class KeyPairJwtEncoderBuilder {
Copy link
Contributor

@jzheaux jzheaux May 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be best to not have an abstract class since that can be quickly complex for builders. As it stands, the amount of reuse is quite small.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Author

@surajbh123 surajbh123 May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @jzheaux
Running ./gradlew :spring-security-oauth2-jose:check is raising a warning that says:
Class KeyPairJwtEncoderBuilder should be declared as final. |  
So I think we should keep the class abstract

@surajbh123 surajbh123 force-pushed the nimbus-ecoder-builder branch from a53bfb3 to 2d1e2c6 Compare May 13, 2025 18:46
@surajbh123 surajbh123 force-pushed the nimbus-ecoder-builder branch from 2d1e2c6 to 24300cb Compare May 13, 2025 19:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants