Categories
Jakarta EE Ktor Security

Comparing JWT Token Usage in Spring Boot, Quarkus, Jakarta, and Kotlin Ktor: A Framework Exploration – Part 4

Since this topic became very extensive, I decided to split up the blog into 4 parts. To keep blog lengths manageable. Here is the split up

Part 1: Introduction
Part 2: Payara, Spring Boot and Quarkus
Part 3: Ktor and Atbash Runtime
Part 4: Discussion and conclusion (this one)

For an introduction around JWT Tokens, you can have a look at the first part of this blog. It also contains a description how the Keycloak service is created for the example programs described in this part.
Part 2 and 3 contains the description of the example application for each runtime.

Discussion

In parts 2 and 3, I showed the most important aspects of using a JWT token with Payara Micro (Jakarta EE), Spring Boot, Quarkus, Kotlin, and Atbash Runtime. The JWT tokens themselves are standardised but how you must use them in the different runtimes is not defined and thus different. Although there exists the MicroProfile JWT Auth specification, even those runtimes that follow it, have differences in how it should be activated and how roles should be verified, especially when you don’t want to check a role. The specification, besides duplicating a few things from the JWT specification itself like how validation needs to be done, only defines how a MicroProfile application should retrieve claim values.

It is obvious that for each runtime we need to add some dependency that brings in the code to handle the JWT tokens. But for several of these runtimes, you also need to activate the functionality. This is the case for Payara Micro through the @LoginConfig and also for Atbash Runtime since the functionality is provided there by a non-core module.

Another configuration aspect is the definition of the location of the certificates. Spring Boot is the only one that makes use of the OAuth2 / OpenId Connect well know endpoint for this. The other runtimes require you to specify the URL where the keys can be retrieved in a certain format. This allows for more flexibility of course and potential support for providers that do not follow the standard in all its extends. But since we are talking about security, it would probably be better that only those certified, properly tested providers would be used as is the case with the Spring Boot implementation.

The main difference in using a JWT token on runtime is how the roles are verified. Not only is not specified which claim should hold the role names, nor is it defined how the authorization should be performed. This leads to important differences between the runtimes.

Within Kotlin Ktor, We should define a security protocol for each different role we want to check and assign it a name. Or you create a custom extension function that allows you to specify the role at the endpoint as I have done in the example. But important to note is that we need to be explicit in each case. Which role or if no role at all is required, we need to indicate this.

This is not the case for the other runtimes, except the Atbash Runtime.

When you don’t use any annotation on the JAX-RS method with Payara Micro and Spring Boot, no role is required, only a valid JWT token. But with Quarkus, when not specifying anything, the endpoint becomes publicly accessible. This is not a good practice because when you as a developer forget to put an annotation, the endpoint becomes available for everyone, or at least any authenticated user for certain runtime. This violates the “principle of least privilege” that by default, a user has no rights and you explicitly need to define who is allowed to call that action. That is the reason why Atbash Runtime treats the omission of an annotation to check on roles as an error and hides the endpoint and shows a warning in the log.

If you do not want to check for a role when using Atbash Runtime, you can annotate the JAX-RS method with @PermitAll. The JavaDoc says “Specifies that all security roles are allowed to invoke the specified method(s)” and thus it is clearly about the authorization on the endpoint. But if you use @PermitAll in Payara Micro, the endpoint becomes publicly accessible, dropping also authentication. That is not the intention of the annotation if you ask me. Although the Javadoc might be to blame for this as it mentions “that the specified method(s) are ‘unchecked'” which might be interpreted as no check at all.

Conclusion

All major frameworks and runtimes have support for using JWT Tokens within your application to authenticate and authorise a client call to a JAX-RS endpoint. When adding the necessary dependency to have the code available and adding some minimal configuration like defining where the keys can be retrieved to verify the signature, you are ready to go. The only exception here might be Kotlin Ktor where you are confronted with a few manual statements about the verification and validation of the token. It is not completely hidden away.

The most important difference lies in how the check for the roles is done. And especially in the case that we don’t require any role, just a valid JWT token. Only Atbash Runtime applies the “principle of least privilege”. On the other runtimes, forgetting to define a check for a role leads to the fact that the endpoint becomes accessible to any authenticated user or even worse, publicly accessible.

There is also confusion around @PermitAll which according to the java doc is about authorization, but in Jakarta EE runtime like Payara Micro, the endpoint also suddenly becomes publicly accessible.

Interested in running an example on the mentioned runtimes, check out the directories in the https://github.com/rdebusscher/Project_FF/tree/main/jwt repo which work with KeyCloak as the provider.

Training and Support

Do you need a specific training session on Jakarta EE, Quarkus, Kotlin or MicroProfile? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Categories
Jakarta EE Ktor Security

Comparing JWT Token Usage in Spring Boot, Quarkus, Jakarta, and Kotlin Ktor: A Framework Exploration – Part 3

Since this topic became very extensive, I decided to split up the blog into 4 parts. To keep blog lengths manageable. Here is the split up

Part 1: Introduction
Part 2: Payara, Spring Boot and Quarkus
Part 3: Ktor and Atbash Runtime (this one)
Part 4: Discussion and conclusion

For an introduction around JWT Tokens, you can have a look at the first part of this blog. It also contains a description how the Keycloak service is created for the example programs described in this part.
Part 2 contains the description for Payara Micro, Spring Boot and Quarkus.

Ktor

Also within Ktor there is some excellent support for using JWT tokens although we need to code a little bit more if we want to have support for rotating public keys and easy checks on the roles within the tokens.

But first, Let us start again with the dependencies you need within your application.

        <!-- Ktor authentication -->
        <dependency>
            <groupId>io.ktor</groupId>
            <artifactId>ktor-server-auth-jvm</artifactId>
            <version>${ktor_version}</version>
        </dependency>
        <!-- Ktor support for JWT -->
        <dependency>
            <groupId>io.ktor</groupId>
            <artifactId>ktor-server-auth-jwt-jvm</artifactId>
            <version>${ktor_version}</version>
        </dependency>

We need a dependency to add the authentication support and another one for having the JWT token as the source for authentication and authorisation.

Just as with the Payara and Quarkus case, we need to define the location to retrieve the public key, expected issuer, and audience through the configuration of our application. In our example application, this is provided in the application.yml file.

jwt:
  issuer: "http://localhost:8888/auth/realms/atbash_project_ff"
  audience: "account"

We programmatically read these values in our own code, so the keys can be whatever you like, they are not predetermined as with the other runtimes. In the example, you see that we also don’t define the location of the public key endpoint as we can derive that from the issuer value in the case of KeyCloak. But you are free to specify a specific URL for this value of course.

Configuration of the modules in Ktor is commonly done by creating an extension function on Application object, as I have also done in this example. This is the general structure of this function

fun Application.configureSecurity() {

    authentication {
        jwt("jwt-auth") {
            realm = "Atbash project FF"
            // this@configureSecurity refers to Application.configureSecurity()
            val issuer = this@configureSecurity.environment.config.property("jwt.issuer").getString()
            val expectedAudience = this@configureSecurity.environment.config.property("jwt.audience").getString()
            val jwkUrl = URL("$issuer/protocol/openid-connect/certs")
            val jwkProvider = UrlJwkProvider(jwkUrl)

            verifier {
        // not shown for brevity              

            }

            validate { credential ->
                // If we need validation of the roles, use authorizeWithRoles
                // We cannot define the roles that we need to be able to check this here.
                JWTPrincipal(credential.payload)
            }

            challenge { defaultScheme, realm ->
                // Response when verification fails
                // Ideally should be a JSON payload that we sent back
                call.respond(HttpStatusCode.Unauthorized, "$realm: Token is not valid or has expired")
            }
        }
    }

}

The function jwt("jwt-auth") { indicates that we define an authentication protocol based on the JWT tokens and we name it jwt-auth. We can name it differently and can have even multiple protocols in the same application as long ask we correctly indicate which protocol name we want at the endpoint.

The JWT protocol in Ktor requires 3 parts, a verification part, a validation one, and lastly how the challenge is handled.

The verification part defines how the verification of the token is performed and will be discussed in more detail in a moment. We can do further validation on the token by looking at the roles that are in the token. If you have many different roles, this leads to many different named JWT protocols. Therefore I opted in this example to write another extension function on the Route object that handles this requirement more generically. And the challenge part is executed to formulate a response for the client in case the validation of the token failed.

The verifier method defines how the verification of the token is performed. We make use of the UrlJwkProvider which can read the keys in the JWKS format which contains keys in a JSON format. But it doesn’t try to reread the endpoint in case the key is not found. This also means we cannot apply rotating keys for signing the JWT tokens which is recommended in production. Therefore, we make use of a small helper which caches the keys but read the endpoint again when the key is not found. This functionality could be improved to avoid a DOS attack by calling your endpoint with some random key ids which would put Keycloak or the JWT Token provider under stress.

            val jwkProvider = UrlJwkProvider(jwkUrl)

            verifier {
                val publicKey = PublicKeyCache.getPublicKey(jwkProvider, it)

                JWT.require(Algorithm.RSA256(publicKey, null))
                    .withAudience(expectedAudience)
                    .withIssuer(issuer)
                    .build()

            }

The other improvement that you can find in the example is the validation part. Since you only have the credential as input for this validation, you can check if the token has a certain role, but you can’t make this check dynamic based on the endpoint. As mentioned, this would mean that for each role that you want to check, you should make a different JWT check.

The example contains an extension function on the Route object so that you can define the role that you expect. This is how you can use this new authorizeWithRoles function

        authorizeWithRoles("jwt-auth", listOf("administrator")) {
            get("/protected/admin") {
                call.respondText("Protected Resource; Administrator Only ")
            }
        }

So besides the name for the protocol we like to use, you can also define a set of roles that you expect to be in the token. The function itself is not that long but a little complex because we add a new interceptor in the pipeline used by Ktor to handle the request. If you want to look at the details, have a look at the example code.

If you just need a valid token, without any check on the roles, you can make use of the standard Ktor functionality

        authenticate("jwt-auth") {
            get("/protected/user") {
                val principal = call.authentication.principal<JWTPrincipal>()
                //val username = principal?.payload?.getClaim("username")?.asString()
                val username = principal?.payload?.getClaim("preferred_username")?.asString()
                call.respondText("Hello, $username!")
            }
        }

This last snippet also shows how you can get access to the claims within the token. You can access the principal associated with- the request by requesting call.authentication.principal<JWTPrincipal>() where you immediately make the cast to the JWTPrincipal class. This contains the entire token content easily accessible from within your Kotlin code as you can see in the example where I retrieve the preferred_username.

You can review all code presented here in the example https://github.com/rdebusscher/Project_FF/tree/main/jwt/ktor.

Atbash Runtime

Atbash Runtime is a small modular Jakarta EE Core profile runtime. So by default, it doesn’t has support for using JWT tokens. But since these tokens are the de facto standard, there is an Atbash Runtime module that supports them so that you can use it for your application.

As a dependency, you can add this JWT supporting module to your project

        <dependency>
            <!-- Adds JWT Support in the case we are using the Jakarta Runner, no addition of the MP JWT Auth API required -->
            <!-- Otherwise, when not using Jakarta Runner, the addition of JWT Auth API as provided is enough if you are using Atbash Runner Jar executable -->
            <groupId>be.atbash.runtime</groupId>
            <artifactId>jwt-auth-module</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

Since we use the Jakarta Runner feature of the Atbash runtime, which allows you to execute your web application through a simple main method, we need to add the module itself. If you run your application as a war file, make sure you activate the JWT module within the configuration so that the module is active.

The JWT support within Atbash runtime is also based on the Microprofile JWT Auth specification, so you will see many similarities with the Payara and Quarkus examples we have discussed in part 2 of this blog.

Configuration requires the 3 values for public key location, expected issuer, and audience.

mp.jwt.verify.publickey.location=http://localhost:8888/auth/realms/atbash_project_ff/protocol/openid-connect/certs
mp.jwt.verify.issuer=http://localhost:8888/auth/realms/atbash_project_ff
mp.jwt.verify.audiences=account

You are also required to indicate the @LoginConfig (in case you are executing your application as a WAR file) so that the JWT Module is active for the application. But there is no need to define @DeclareRoles as Atbash Runtime takes the value of the individual @RolesAllowed as valid roles.

A difference with Payara, for example, is that you need to add @PermitAll to a method when you don’t want to check on any roles. Within Atbash Runtime there is the “principle of least privilege” implemented. If you don’t specify anything on a JAX_RS method no client can call it. This is to avoid that you forget to define some security requirements and expose the endpoint without any checks. The JavaDoc says “Specifies that all security roles are allowed to invoke the specified method(s)” and thus it is clearly what we need. Although, some runtimes, including Payara, interpret this differently and I’ll go deeper on this topic in part 4.

The example code is located at https://github.com/rdebusscher/Project_FF/tree/main/jwt/atbash.

Discussion

In the last part of the blog, I’ll have a discussion about similarities and differences. These differences are especially important when you don’t want to have a check on a role within the token.

Part 1: Introduction
Part 2: Payara, Spring Boot and Quarkus
Part 3: Ktor and Atbash Runtime (this one)
Part 4: Discussion and conclusion

Training and Support

Do you need a specific training session on Jakarta EE, Quarkus, Kotlin or MicroProfile? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Categories
Jakarta EE Ktor Security

Comparing JWT Token Usage in Spring Boot, Quarkus, Jakarta, and Kotlin Ktor: A Framework Exploration – Part 2

Since this topic became very extensive, I decided to split up the blog into 4 parts. To keep blog lengths manageable. Here is the split up

Part 1: Introduction
Part 2: Payara, Spring Boot and Quarkus (this one)
Part 3: Ktor and Atbash Runtime
Part 4: Discussion and conclusion

For an introduction around JWT Tokens, you can have a look at the first part of this blog. It also contains a description how the Keycloak service is created for the example programs described in this part.

Payara

As an example of how you can work with JWT tokens with Jakarta EE and MicroProfile, we make use of Payara Micro.

The JWT Token support is provided by MicroProfile, so add the dependency to your project.

        <dependency>
            <groupId>org.eclipse.microprofile</groupId>
            <artifactId>microprofile</artifactId>
            <version>6.0</version>
            <type>pom</type>
            <scope>provided</scope>
        </dependency>

We are using MicroProfile 6, which requires Jakarta EE 10 runtime as this is the version that is supported by the Payara Micro community edition.

As configuration, we need to provide the endpoint where the MicroProfile JWT Auth implementation can retrieve the public key that is required to validate the content of the token against the provided signature. This can be done by specifying mp.jwt.verify.publickey.location configuration key. Two other configuration keys are required, one that verifies if the issuer of the token is as expected and the audience claim is the other one.

Other configuration aspects are the indication that a JWT token will be used as authentication and authorization for the endpoints through the @LoginConfig annotation. The @DeclareRoles annotation is a Jakarta EE annotation that indicates which roles are recognised and can be used. These annotations can be placed on any CDI bean.

@LoginConfig(authMethod = "MP-JWT")
@DeclareRoles({"administrator"})

On the JAX-RS method, we can add the @RolesAllowed annotation to indicate the role that must be present in the token before the client is allowed to call the endpoint.

    @GET
    @Path("/admin")
    @RolesAllowed("administrator")
    public String getAdminMessage() {

When there is no annotation placed on the method, only a valid JWT token is required to call the endpoint. Also, have a look at the part 4 of this blog for some important info and differences between runtimes.

Through the MicroProfile JWT Auth specification, we can also access one or all the claims that are present in the token. The following snippet shows how you can access a single claim or the entire token in a CDI bean or JAX-RS resource class.

    @Inject
    @Claim("preferred_username")
    private String name;

    @Inject
    private JsonWebToken jsonWebToken;
    // When you need access to every aspect of the JWT token.

The entire example can be found in the project https://github.com/rdebusscher/Project_FF/tree/main/jwt/payara.

Spring Boot

Also, Spring Boot has excellent support for using JWT tokens for the authentication and authorization of rest endpoints. Besides the Spring Boot Security starter, the Oauth2 Resource Server dependency is required within your application. So you don’t need to handle the JWT token yourself in a programmatic way as some resources on the internet claim.

In our example, we use Spring Boot 3 and JDK 17.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
    </dependency>

In contrast to MicroProfile where you need to provide several configuration keys, Spring boot makes use of the OpenId Connect specification where it is defined that the endpoint .well-known/openid-configuration provides all info. This includes the location of the public key required for the validation of the token against the signature and the value of the issuer. The location can be specified through a Spring Configuration resource.

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8888/auth/realms/atbash_project_ff
spring.security.oauth2.resourceserver.jwt.audiences=account

The audience value is not required to be defined, Spring Boot works without it. But it is a recommended configuration aspect to make sure that tokens are correctly used, especially when you use tokens for multiple applications.

You can either define the requirements for the roles that should be present in the token using a Spring Bean that extends the WebSecurityConfigurerAdapter class and the HttpSecurity builder, but I prefer the method-based approach.
With this approach, you can define the required role using the @PreAuthorize annotation

    @GetMapping("/admin")
    @PreAuthorize("hasAuthority('administrator')")
    public String getAdminMessage() {

It makes it easier to find out which role is required before a client can call the endpoint and also easier to verify if you didn’t make any error in the security configuration of your application. This method-based approach requires a small activation and mapping between the roles within the token and the authority we check in the annotation.

@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
}

The configuration for the JWT token roles is provided by a JwtAuthenticationConverter bean.

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        grantedAuthoritiesConverter.setAuthorityPrefix("");
        grantedAuthoritiesConverter.setAuthoritiesClaimName("groups");

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }

Within the REST methods, we can have access to the JWT token claims, just as with the Jakarta EE and MicroProfile example. We need to add a JwtAuthenticationToken parameter to the method which allows access to claims through the getTokenAttributes() method.

    @GetMapping("/user")
    public String getUser(JwtAuthenticationToken authentication) {
        Object username = authentication.getTokenAttributes().get("preferred_username");

The entire example can be found in the project https://github.com/rdebusscher/Project_FF/tree/main/jwt/spring.

Quarkus

The Quarkus support is also based on MicroProfile, so you will see several similarities with the Payara case I described earlier. The Quarkus example is based on the recent Quarkus 3.x version. As a dependency, we need two artifacts related to the JWT support provided by the SmallRye project. Although it seems you do not need the build one at first sight, as it is about creating JWT tokens within your application, the example did not work without it.

        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-jwt</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-jwt-build</artifactId>
        </dependency>

Since the SmallRye JWT implementation is also using the SMicroProfile JWT auth specification, the configuration through key-value pairs is identical to the Payara one. We need to define the location of the publicKey, and the expected values for the issuer and audience. In the example, I have defined them in the application.properties file, a Quarkus-specific configuration source. But as long as they can be retrieved through any of the supported configuration sources, it is ok.

Since Quarkus is not a Jakarta-compliant runtime, it doesn’t require any indication that the application will make use of the JWT tokens for authentication and authorisation. The existence of the two dependencies we added earlier to the project is enough. In this case, it is similar to the Spring Boot case where we also did not do this.

On the JAX-RS resource methods, we can indicate if we need a certain role within the token, or that just the token itself is required and no specific role is required. If a role is required, we can make use of the same @RolesAllowed annotation we encountered in the Payara example or we need to add the @Authenticated annotation if we just need a valid token.

    @GET
    @Path("/admin")
    @RolesAllowed("administrator")
    public String getAdminMessage() {
        return "Protected Resource; Administrator Only ";
    }

    @GET
    @Path("/user")
    @Authenticated
    // No roles specified, so only valid JWT is required
    public String getUser() {
        return "Protected Resource; user : " + name;
    }

This @Authenticated annotation is defined in the Quarkus Security artifact, brought in transitively, and indicates that an authenticated user is required. Without this annotation, the endpoint would become publicly accessible, without the need for any token or authentication method.

More on that in a part 4 of this blog.

The retrieval of the claims is again identical to the Payara case. The example project can be found at https://github.com/rdebusscher/Project_FF/tree/main/jwt/quarkus.

Runtimes

The Ktor and Atbash Runtime versions of the example application are described in part 3.

Part 1: Introduction
Part 2: Payara, Spring Boot and Quarkus (this one)
Part 3: Ktor and Atbash Runtime
Part 4: Discussion and conclusion

Training and Support

Do you need a specific training session on Jakarta EE, Quarkus, Kotlin or MicroProfile? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Categories
Jakarta EE Ktor Security

Comparing JWT Token Usage in Spring Boot, Quarkus, Jakarta, and Kotlin Ktor: A Framework Exploration – Part 1

Since this topic became very extensive, I decided to split up the blog into 4 parts. To keep blog lengths manageable. Here is the split up

Part 1: Introduction (this one)
Part 2: Payara, Spring Boot and Quarkus
Part 3: Ktor and Atbash Runtime
Part 4: Discussion and conclusion

But don’t worry, all these 4 parts will be released within the same week so that those people that are eager to process it in one go, do not need to wait a long time before the series is published.

Introduction

As the demand for secure and efficient authentication and authorization mechanisms grows, JSON Web Tokens (JWT) have emerged as a favored choice for developers. JWT tokens provide a modern approach to verifying user identity and defining access privileges within web applications. In this blog post, we will delve into the usage of JWT tokens across various frameworks, namely Spring Boot, Quarkus, Jakarta, and Kotlin Ktor. By comparing their implementation approaches, we aim to provide insights into how JWT tokens are utilized within each framework and help you make a transition from one to another easier.

Understanding the Basics of JWT Tokens

At the core of JWT tokens lies a simple yet powerful structure that encompasses all the necessary information for secure authentication and authorization. Let’s dive into the basics of JWT tokens and explore their three essential components: the header, the body, and the signature.

1. Header

The header of a JWT token contains metadata about the token itself and the algorithms used to secure it. It typically consists of two parts: the token type, which is always “JWT,” and the signing algorithm employed, such as HMAC, RSA, or ECDSA. This header is Base64Url encoded and forms the first part of the JWT token.

2. Body (Payload):

The body, also known as the payload, carries the actual data within the JWT token. It contains the claims, which are statements about the user and additional metadata. Claims can include information like the user’s ID, name, email, or any other relevant data. The payload is also Base64Url encoded and forms the second part of the JWT token.

3. Signature

The signature is the crucial component that ensures the integrity and authenticity of the JWT token. It is created by combining the encoded header, the encoded payload, and a secret key known only to the server. The signature is used to verify that the token has not been tampered with during transmission or storage. It acts as a digital signature and prevents unauthorized modifications to the token. The signature is appended as the third part of the JWT token.

Self-Contained and Secure

One of the significant advantages of JWT tokens is their self-contained nature. Since all the necessary information is embedded within the token itself, there is no need for additional database queries or session lookups during authentication and authorization processes. This inherent characteristic contributes to improved performance and scalability.

To verify the authenticity and integrity of a JWT token, the recipient needs access to the public key or shared secret used to generate the signature. By retrieving the public key or shared secret, the recipient can verify the token’s signature and ensure that no tampering or unauthorized modifications have occurred. This mechanism provides a robust security layer, assuring that the token’s contents can be trusted.

User Roles in JWT Tokens

JWT tokens can also include user roles as part of their payload. User roles define the permissions and privileges associated with a particular user. By including this information in the JWT token, applications can determine the user’s authorization level and grant or restrict access to specific resources or functionalities accordingly. This granular approach to authorization allows for fine-grained control over user permissions within the application.

In the upcoming sections, we will explore how different frameworks incorporate these fundamental JWT token concepts into their authentication and authorization workflows. Understanding the core principles behind JWT tokens sets the stage for a comprehensive comparison, enabling us to evaluate the strengths and nuances of each framework’s implementation.

Example application

The same example application is made with different runtimes. It contains a couple of endpoints, they all require a valid token before they should be executed. One of the endpoints requires that the token contains the role of administrator.

GET /protected/user -> Hello username
GET /protected/admin -> Protected Resource; Administrator Only

The tokens utilised in our example are sourced from Keycloak, a reliable and widely adopted Authorization provider. Keycloak offers various standard flows for obtaining these tokens, catering to diverse authentication scenarios.

One of the commonly employed flows is the authorization code flow, which involves user interaction through dedicated screens provided by the Authorization provider. Users are prompted to log in and provide their credentials, following which Keycloak generates the necessary tokens for authentication and authorization purposes.

Alternatively, Keycloak supports a username and password-based approach where users can submit their credentials to a designated endpoint. This method allows Keycloak to validate the provided information and issue the relevant tokens required for subsequent authentication and authorization processes.

For our example, a custom realm with a configuration that is suitable for all our runtimes is created by setup_jwt_example.py and can be found in the directory https://github.com/rdebusscher/Project_FF/tree/main/jwt/keycloak. The script prepares the realm and a OpenId Connect client so that in response to a valid user name and password combination, a JWT token with the roles of the user is returned. It creates also two users, one of them having the admin role.

The Python script test_jwt_example.py can be used to test out the solution in each of the runtimes. It calls both endpoints with the two users that are defined. And so, one of the calls will result in an error since the non-administrator user is not allowed to call the administrator endpoint.

Runtimes

The different runtimes are discussed in part 2 and part 3 of this series.

Part 1: Introduction (this one)
Part 2: Payara, Spring Boot and Quarkus
Part 3: Ktor and Atbash Runtime
Part 4: Discussion and conclusion

Training and Support

Do you need a specific training session on Jakarta EE, Quarkus, Kotlin or MicroProfile? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Categories
Jakarta EE Security

Where is the Security Specification in Jakarta EE Core profile?

The Jakarta EE Core profile targets the smaller runtimes, those that focus on delivering support for applications with REST endpoints only. For that reason, the main specifications included in the profile are the JAX-RS specifications and the specifications around JSON, JSON-P and JSON-B.

Another very important specification was targeted for Jakarta EE 10 and this new core profile, Jakarta config. The discussion about it is still going on. It is thus not included yet. That is the reason why the Atbash Runtime includes an implementation of the MicroProfile Config specification for the moment.

But there is another very important aspect that seems to be forgotten for this core profile, security.

Jakarta Security specification

For the moment, there are no plans to include a security-related specification in this core profile and this is a missed opportunity. Security is a very important aspect of each application but as usual, it doesn’t receive enough attention. Although there were several major security issue events in the last 6 months, we all remember the log4shell vulnerability.

Within the Jakarta family, we have the Jakarta Security API specification which combines the more low-level specifications Authentication and Authorisation. It also makes it easier for the developer to define the requirements without relying on the runtime to perform the setup. Within cloud environments, we should be able to start our application without extensive setup of the server or runtime.

But this Security API specification is not really a good candidate to be included in the Core profile. First of all, it depends on many other specifications and some of them are archaic and not really adapted to be used in these small runtimes targeted to the cloud.

But most of all, there is no support for the token-based security that is typically used in microservices and the applications that stands as a model for these Core profile runtimes.

The JSON Web Tokens (JWS token specification) are an ideal means to carry the information about the user on the requests. Within the header, a cryptographically signed JSON carries the information about the identity and the permissions of the user who made the (initial) call to the environment. With the signature, we can be sure the content is not tampered with and we can make use of the groups and role information available in the token to determine if the user is allowed to perform the action.

And we only need to use the public key of the key pair to verify the signature. This public key can be kept cached and only occasionally refreshed from the process that creates those JWT tokens. The verification and the usage of the JWT tokens do not require calls to external systems and are ideal to implement performant systems.

MicroProfile Security specification

This is a major gap in the support of technologies for Jakarta EE that calls itself ‘Cloud-native Enterprise Java’. But also within MicroProfile, there is no adequate support for JWT tokens. You have the MicroProfile JWT Auth specification that defines functionality for the MicroProfile runtimes that are using JWT tokens.

But there are several restrictions on top of the JOSE (Javascript Object Signing and Encryption) specification that make the MicroProfile specification less useful. The configuration is defined so that an application can only be used by one token emitter. You can define only one issuer, only one signature algorithm, you can indicate that a JWS or JWE token is used but not both at the same time, etc.

This makes that applications that make use of the MicroProfile JWT Auth specification are in fact part of a distributed monolith. You cannot use it in a real microservices situation as it will be called by many different applications, including external systems using different token providers.

Side note, since the majority of the companies that claim to do microservices are actually just building a distributed monolith, the problem with the MicroProfile JWT Auth specification is not impacting the majority I guess.

Security in Atbash Runtime

Since security is an important and vital aspect of any application, the upcoming version of the Atbash runtime will have support for JWT tokens. It is based on the JOSE specifications and for convenience, is using the MicroProfile JWT Auth API but not having the many restrictions of the MicroProfile Specification.

The configuration parameters are interpreted slightly differently, like the key defining the issues is interpreted as a list. This still allows the developer to only allow tokens from one source, but also allows the application to be used in real microservices situations and accept multiple issuers.

A first draft of the implementation is included in the main branch of the GitHub repository and will be refined in the coming weeks before the release is made.

Conclusion

Although security is a very important aspect of each application, specifications, and products should help the developers to implement the security requirements they have. There is no Security specification included, nor is there one planned in the new upcoming Core profile of Jakarta EE.

In fact, Jakarta EE does not have a specification that is useful in cloud-native environments since there is no support for JWT tokens.

But also the MicroProfile security specification is not a solution in all cases since it restricts the tokens to one issuer. Making it only useable in a distributed monolith situation and not in a real large-scale microservices environment.

The JWT token implementation within Atbash Runtime is based on the API of MicroProfile JWT Auth but overcomes the limitations of the MicroProfile specification by supporting the configuration of multiple sources, multiple algorithms, and using JWT and JWE tokens at the same time.
Sounds good? Give it a try when the next version will be available in a few weeks.

Categories
Atbash Java EE MicroProfile Security

Winter 2018 Release train for Atbash libraries

Introduction

Atbash is a set of libraries which tries to make security easier. It contains features around cryptographic keys (RSA and EC keys for example), reading and writing keys in all the different formats, algorithms (like the Diffie-Hellman Algorithm), securing JAX-RS endpoints, and many more. The top-level project is Atbash Octopus which is a complete platform for declarative security for Java SE, Java EE and Micro-Services (MicroProfile).

Besides this security focus point, some useful Java SE and MicroProfile extensions are also made available instead of just bundling them with the security features which uses them.

A new set of Atbash features are ready and are released to Maven Central. This blog post gives a short overview of the different new features but in the coming weeks, the main features are described in more detail in separate blogs.

Winter 2018 release

The first time I released multiple Atbash libraries to Maven Central was last summer, with the Summer 2018 release. Also now there are multiple features ready so I found it a good time to release again a lot of the libraries together.

The 3 main top features in this winter release are

  • Reading and writing many cryptographic key types (RSA, EC, OCT, and DH) in many formats like PEM (including different encodings like PKCS1 and PKCS8), JWK, JWKSet, and Java KeyStore (‘old versions’ and PKCS12).
  • Easy encryption helper methods (using symmetric AES keys) and methods for creating JWE objects (Encrypted JWT tokens).
  • Implementation of the Diffie-Hellman algorithm to exchange data in an encrypted way without the need for exchanging the key.

The non-security highlights in this release are

  • An extensible Resource API
  • The option to define alternative keys for MicroProfile config.

More on them in the next section and in the upcoming dedicated blogs

Supported Java versions.

The goal of Atbash is also to bring you all those goodies, even when you are still stuck with Java 7. Therefore are libraries support Java 7 as the minimum version. But with the release of JDK 11 a few months ago, I made sure that all the libraries which are released as part of this Winter 2018 release, can also run on this new Long Term supported Java version. It only supports the Class-Path fully, there are no Java modules created.

New features

Atbash Utils
A library containing some useful utilities for Java SE, CDI, and JSF. In the new 0.9.3 version, there are 2 major new features implemented.

Resource API

With the resource API, it becomes easy to retrieve the content of a file, a class path resource, a URL or any custom defined location in a uniform way.

Based on the prefix, like classpath:, http:, etc …, the implementation will look up and read the resource correctly. But you can also define your own type, defining your custom prefix, and register is through the Service Loader mechanism.

This uniform reading of a resource is very handy when you need to read some resource content in your application (or your own framework) and wants to make the location of the file configurable.
You just need to retrieve the ‘location’ from the configuration and can call

ResourceUtil.getInstance().getStream();

Resource Scanner

Another addition, also related to resources, is the resource scanner to retrieve all class path resources matching a certain pattern. There are various use cases where it could be handy that you can retrieve a list of resources, like all files ending on .config.properties, from the class path. This allows having some extension mechanism that brings their own configuration files.

The alternative keys for MicroProfile config make use of this.

Atbash Config

The library contains a few useful extensions for MicroProfile Configuration. It has also an implementation of the MicroProfile Config 1.2 version for Java 7. It is a compatible version (not certified) as MP Config requires Java 8.

Alternative keys

A useful addition in this release is the alternative keys which can be defined. Useful in case you want to change the name of a config parameter key but you want to the users of your library the time to adapt to your new key value.
So you can basically define a mapping between 2 keys, old value, and a new value. Within your library code, you already use the new value. But when the Atbash Config library is on the classpath, the developer can still use the old name.

Atbash JWT support

A library to support many aspects of the JWT stack. Not only signed and encrypted JWT tokens, but also JWK (storing keys). And by extension, it has extensive support for cryptographic keys.

In this library, the most notable new features of this winter release can be found.

Reading and writing many cryptographic keys

Reading and writing cryptographic keys was already partially implemented in the previous version of the library. But now it supports more or less all possible scenarios.

On the one side, there is support for the different types

  • RSA keys
  • Elliptic Curve (EC) keys
  • OCT keys (just a set of bits usable for HMAC and AES)

And this can be stored (and read) in many formats

  • Asymmetric private keys in PEM (using PKCS1, PKCS8 or none encoding), JWK, JWKSet, and KeyStores (JKS, JCEKS, and PKCS12)
  • Asymmetric private keys in PEM, JWK, JWKSet, and KeyStores (JKS, JCEKS, and PKCS12)
  • OCT keys as JWK and JWKSet.

And this reading and writing are performed by a single method (all checking is done behind the scenes) namely KeyWriter.writeKeyResource and KeyReader.readKeyResource.

More info in a later blog post.

Encryption

There are various helper methods to perform encryption easier. The encryption can be performed with an OCT key, or this key can be generated based on a password or passphrase using the Key Derivation Functions.

A second possibility for encryption is the use of JWE, an encrypted version of the JWT token. In the blog post, an example will be given how you can easily create a JWE. And it is the same method to create a JWT, without encryption, just a different parameter.

Key Server

The Key Server is more an example of the Diffie-Hellman algorithm which is implemented in this release more than it is a production-ready component. With the Diffie-Hellman algorithm, it is possible to perform encryption of the data without the need to exchange the secret key. The same principle is used for SSL communication. But now it is performed by the application itself and thus no termination like the one performed by the firewalls is possible.

The blog will explain the Key Server principals and another use case where the posting of JSON data to an endpoint is transparently encrypted.

Future

More features from Octopus are also transferred from the original Octopus to the Atbash Octopus framework, including the support for OAuth2 and OpenId Connect. But there wasn’t enough time to migrate all the features that I wanted, so Octopus isn’t released is this time. But migration and improvements will continue to be performed.

With the release of JDK 11, more focus will be placed on this new version and thus the master branch will hold only code which will be compatible with Java 8 and Java 11. The support for Java 7 will be transferred to a separate branch and will go in maintenance mode. No real new features will be implemented anymore in this branch unless required for Atbash Octopus which will stay at Java 7 for a little longer.

Since the first application server supporting JDK 11 is released, and more of them coming in the next months, more focus will be placed on that runtime environment.

Conclusion

As you can read in the above paragraphs, this release contains some interesting features around security and configuration. And the good thing, they all can be used already with JDK 11 in Class-Path mode.

In the follow-up blogs, the mean new features will be discussed more in details, so I hope to welcome you again in the near future.

Have fun.

Categories
Atbash Overview Security

Atbash Summer release train

Introduction

All the Atbash repositories are still under heavy development, that is why they are released in one go. The last few days, such a release of almost all libraries is performed.

This gives a short overview of what you can find.

Big features

The big feature changes can be found in

  • Atbash JWT support related to cryptographic key support.
  • Atbash Rest client, a Java 7 port of the MicroProfile spec.
  • And Atbash Octopus where KeyCloak and MicroProfile JWT auth spec and interoperability between schemes are central in this release.

Cryptographic key support

Since there are many formats in which keys can be persisted (PEM, Java Key Stores, JWK, etc …), they are all internally stored as an AtbashKey. It contains the Key itself (as Java object), the identification and the type of the key (like RSA, private or public part, etc …)

Creating such keys can be achieved by using the class KeyGenerator, with the method generateKeys(). This class is available as CDI instance or can be instantiated directory in those environments/locations where no CDI is available.

The parameter of the generateKeys() method, defines which key(s) is created. This parameter can be created using a builder pattern.

RSAGenerationParameters generationParameters = new RSAGenerationParameters.RSAGenerationParametersBuilder()
        .withKeyId("the-kid")
        .build();
List<AtbashKey> atbashKeys = generator.generateKeys(generationParameters);

In the above example, multiple keys are generated since RSA is an asymmetric key and thus private and public parts are generated.

Writing of a key can be performed with the KeyWriter class. It has a method, writeKeyResource, which can be used to persist a key into one of the formats. The format is specified as a parameter of type KeyResourceType. This can indicate the required format like PEM, Key store, JWK, etc…

The specific type of PEM (like PKCS1, PKCS8, etc …) is defined by the configuration parameters.

Another parameter defines the password/passphrase for the key (if needed) and one for the file as a whole in the case of the Java KeyStore format for example.

The last functionality around key is then reading of all those keys in the supported format. This functionality is implemented in the KeyReader class. It is again a CDI bean which can be instantiated when no CDI environment is available.

It contains a readKeyResource() method which can read  all the keys in a resource (like PEM file, Java Key Store, JWK, etc …) As a parameter, an instance of KeyResourcePasswordLookup is supplied which retrieves a password in those case where it is needed (to read the file or decrypt the key)

The return of the method is a list because a resource can contain more than one key AtbashKeys.

This Key support is an initial version and will be improved in the further releases of the atbash-jwt-support releases with more features and more supported formats.

Atbash Rest Client

A first release was done mid-June and contained an implementation in Java 7 for Java SE and Java EE which is compatible with the MicroProfile Rest Client specification. (see here) It allows you to ‘inject’ or create (useful in Java SE environments) a system generated Rest client based on the definition of your JAX-RS endpoint defined in an interface class.

In this release, the RestClientBuilderListener from the MP Rest Client spec 1.1 is added and implemented so that we can define some additional providers in a general way. This is important for the Octopus release so that we can add the credentials, stored within the Octopus context, to the JAX-RS call automatically. Without the need to specify the providers manually.

Atbash Octopus

And of course, many new features are added to Octopus. They are migrated from the old Octopus or newly added.

The highlights are:

– Added support for KeyCloak server. JSF applications can use the authentication and authorization from KeyCloak configured realms. Also, the AccessToken from it can be based on in the header of other request and verified by JAX-RS endpoints. The only thing which is needed is the location of the KeyCloak server and the realm config in JSON (which is supplied by KeyCloak)

– The SPI option to pass the expected password for a user can now handle hashed passwords. Both the ‘standard’ algorithms from MessageDigest, like SHA-256 but also the key derivation function PBKDF2 can be defined easily.

– The authorization annotations, like @RequiresPermissions, can be specified on JAX-RS methods without the need to define those resources as CDI or EJB beans.

– Authentication and authorization information can be converted automatically to an MP JWT Auth compliant format and used in calls to JAX-RS endpoints. This makes it possible for example integrate JAX-RS resources protected by KeyCloak and MP JWT seamless.

And too much other features to describe here in detail. The user manual is also started and will be announced soon.

Overview all released frameworks

Utilities : 0.9.2

Set of utilities for Java SE, CDI and plain JSF which are very useful in many projects running in one of these environments.

  • Added utility class for HEX encoding (next to the BASE64 encoding)
  • Added support for byte arrays and encoding (HEX and BASE64) through the ByteSource class.

JSON-smart : 0.9.1

A small library (for Java 7) which can convert JSON to Java instances and vice versa.

  • Added support for @JsonProperty to define the name of JSON property.
  • Contains an SPI so that other naming annotations (like Jackson one) can be used.

Abash-config : 0.9.2

Extension for the MicroProfile Config implementations. Also a Java 7 port of Apache Geronimo Config.

  • Configuration for the base name (with serviceLoader class) is optional.
  • Port of MicroProfile Config 1.3 features to Java 7.

JWT Support : 0.9.0

Convert Java instances to JWT and vice versa and extensive support for Cryptographic keys (reading, writing, creating) supporting multiple types (like RSA, EC, and HMAC keys) and formats (like JWK, JWKSet, PEM, and KeyStore)

  • Support for reading and writing multiple formats (PEM, KeyStore, JWK and JWKSet).
  • Better support for JWT verification with keys using the concepts of KeySelector and KeyManager.

Atbash config server : 0.9.1

Configuration source for MP Config as a server supplying config through JAX-RS endpoints.

  • Added Payara micro as supported server to serve the configuration.

Atbash Rest Client : 0.5.1

Rest client implementation for Java 7.

  • Included RestClientBuilderListener from MP Rest Client 1.1 (to be able to define providers globally)

Octopus : 0.4

  • Integration with Keycloak (Client Credentials for Java SE, AuthorizationCode grant for Web, AccessToken for JAX-RS)
  • Supported for Hashed Passwords (MessageDigest ones and PBKDF2)
  • Support for MP rest Client and Providers available to add tokens for MP JWT Auth and Keycloak.
  • Logout functionality for Web.
  • Authentication events.
  • More features for JAX-RS integration (authorization violations on JAX-RS resource [no need for CDI or EJB], correct 401 return messages, … )
  • Support for default user filter (no need to define user filter before authorizationFilter)

Conclusion

The release contains a lot of goodies related to secure. In the comings months, new features will be added, support for Java 8 and 11 are planned and user manuals and cookbooks will be available to get you started with all those goodies.

The Atbash repositories with some more info and the code of course, can be found at GitHub.

Have fun.

Categories
JavaFX Security

Declarative Security permissions for Java FX FXML Views

Introduction

Using declarative permissions is the preferred way to have security constraints on GUI elements, separately from business logic and fine-grained because we are using individual permission/access rights.
With JavaServer Faces, you can add custom tags, but also within JavaFX FXML views you can do something similar. And recently I found the time to create a POC for an idea that I had some years ago.

Declarative Permissions

When defining indications within the view of your application, GUI elements can be active or not visible depend ending on the permissions the user has. By placing them within your view markup you have 2 major benefits:

  • The ‘definition’ when an element should be visible is done on the element itself and thus it is very clear on which element it operates
  • It isn’t mingled with your other business code.

Also, using permissions is much better and easier to work with than roles. When each feature is assigned specific permissions which the user needs to have, no application redeploy is needed when a feature can now be used by more users. You just have to assign the permission to the users.
Roles are assigned to users and thus when a single feature needs to be accessible by a group of people, you can’t assign the role to them as it would give them also access to other features. And thus you end up changing the code.

JavaFX FXML

With the FXML views, you can define how the view is presented to the end users. Just as with JavaServer Pages tags, it is also possible to create your own custom components to tailor the system to your needs.
Adding additional information to the standard components is also possible by means of the userData tag.

<Button mnemonicParsing="false" onAction="#permission" text="Only admin permission">
  <userData>
    <RequiresPermissions value="admin"/>
  </userData>
</Button>

You can add there information which can be used by your code later on. You can even add multiple data sets by using the JavaFX Collections.

Here in our case we add an instance of the Octopus JavaFX RequiresPermissions class and specify that the user needs the permission admin before the button will be visible.

In order to make this work, you need also to import the class (just as within regular java code)

<?import be.atbash.ee.security.octopus.javafx.authz.tag.RequiresPermissions?>

Processing the view

In order to remove the component from the view when the user hasn’t the required permissions, we need to scan the view just before it is ‘rendered’ to the screen.
This can be easily achieved by the excellent framework afterburner which was a method getView() defined within the FXMLView class.

Octopus defines a child class, be.atbash.ee.security.octopus.javafx.SecureFXMLView, which performs the following steps when the view is retrieved

  • Scan all view nodes
  • Verifies if there is an instance of the RequiresPermissions within the userData section
  • Checks if the user has the permission or not.
  • If (s)he has not, sets the node to not visible (node.setVisible(false) )

Octopus Java FX support

Next, to the processing of the view, Octopus has now also support for JavaFX views by helping the developer in creating a Login screen and the integration with the Octopus Core classes which can perform the required steps for authentication and authorization.

This support is built on top of the afterburner framework which makes developing JavaFX applications easy.

You can start the application by using the start method from the be.atbash.ee.security.octopus.javafx.SecureFXMLApplication class.

SecureFXMLApplication.start(stage, new LoginView(), new UserPageView());

The loginView itself must extend from be.atbash.ee.security.octopus.javafx.LoginFXMLView and contain components with ids user, password and loginButton.

The start() method must also a receive an instance of the first real page which will be shown after the user is authenticated. This is a FXML view where the java class extends from be.atbash.ee.security.octopus.javafx.SecureFXMLView. These views are scanned for their contents within the userData section and component are hidden when the user doesn’t have the required privileges.

Page navigation can be performed by be.atbash.ee.security.octopus.javafx.SecureFXMLApplication#showPage and this method also perform the hiding of components based on the authorization info of this view.

Demo

The Atbash Octopus framework code has a demo application to show this feature in action. It can be found at GitHub .

For those who want to have a quick idea how it looks like, I recorded also a short video which demonstrates the demo

Conclusion

Declarative permissions can also be used on JavaFX FXML views, just as you can do this with JavaServer Faces. Instead of custom tags who defines the required permissions, you have to define the permission requirements within the userData section which is available within each JavaFX component.
This is only a POC and the functionality will be extended in future versions of Atbash Octopus (as the rewrite of the Octopus framework is currently still in Alpha)

Have fun with it

Categories
JAX-RS Security

Http Signatures for End-To-End protection

Introduction

In a distributed system, it is not enough to know who called you, but you also need to make sure the data which you receive are not altered in transit.
The use of tokens (like Bearer JWT tokens according to the OpenId Connect or MicroProfile JWT Auth specifications) can be captured and briefly used to send fake request to your endpoints.
And SSL (like using HTTPS connections) cannot guarantee that your message isn’t read or altered by someone due to the possibilities of SSL Proxy termination or even Man In The Middle Intermediates who fakes correct SSL.

Encryption or protection

The solution is that your processes somehow make sure that the communication between them is ‘safe’. If the processes themselves (your client and your server application) perform these tasks and not some network layer (SSL, in fact, doesn’t fit in any OSI based network layer) of your (virtual) machine.

When your process is performing these tasks, it is possible to create a security context between the 2 processes and then each process can ensure or detect that the message is unaltered or can’t be read, regardless of any Proxy or Man In The Middle.

Whether you need encryption of the message (because you have confidential data) or signing (sensitive data) depends on the data. But at least you need the signing of the message so that we can guarantee the message is actually sent by someone we trust and isn’t altered.

Http Signatures

There is a standard created to have a mechanism to implement this kind of End To End Protection. It is called ‘HTTP Signatures’ and designed by the IETF. https://tools.ietf.org/id/draft-cavage-http-signatures-09.html

It is very HTTP friendly as it adds an additional header to the request. Within this header, enough information is placed so that the target process can verify if the message was altered or not.

This header is called Signature

Signature : keyId="rsa-key-1",algorithm="rsa-sha256",headers="(request-target) date digest content-length",signature="Base64(RSA-SHA256(signing string))"

The specification defines the structure and framework how the header should be created and the verification process must be performed but leaves a lot of freedom to the developer to tailor this protection to his/her needs.

The idea is that there is a signature calculated based on some parameters which are mostly just other headers. And by calculating them on the receiving side, we can verify if these values are changed.

These parameters are described within the headers parameter, and in the example, the following headers are used (request-target) date digest content-length

The values of these headers are concatenated (there exist a few rules about how to do it) and for this string, the cryptographic hash is calculated the signature.
The calculation is done using the value specified by the algorithm parameter (rsa-sha256 in our example above) and from the key with id rsa-key-1 is used for this in our example.
There are a number of crypto algorithms allowed, but RSA keys are the most popular ones.
This signature value is a binary, so a BASE64 encoded value is placed within the header.

In theory, all headers can be used but some of them are more interesting than other ones.
For example

  • (request-target) is a pseudo header value containing the concatenation of the http method and the path of the URL. If you like to include also the hostname (which can be interesting if you want to make sure that a request for the test environments never hits production, you can add the host header in the set of headers which needs to be used for the calculation of the signature value.
  • date header contains the timestamp when the request is created. This allows is to reject ‘old’ requests.
  • digest is very interesting when there is a payload send to the endpoint (like with put and post). This header contains then the hash value of the payload and is a key point in the end to end protection we are discussing here.

On the receiving side, the headers which are specified within the signature parameter are checked. Like

  • date is verified to be within a skew value from the system time
  • digest is verified to see if the header value is the same as the calculated hash value from the payload.

And of course, the signature is verified. Here we decode the signature value with the public key defined in the keyId parameter, using the algorithm specified.
And that value should match the calculated value from the headers.

Using Http Signatures

I have created a Proof Of Concept how this can be integrated with JAX-RS, client and server side.

For the server side, it is quite simple since you can add filters and interceptors easily by annotating them with @Provider. By just then indicating if this interceptor and filters should need to do their work, verification of the HTTP Signature, you are set to go.

@Path("/order")
@RestSignatureCheck
public class OrderController {

On the client side, you can register the required filters and WriterInterceptor manually with the Client. Something like this

Client client = ClientBuilder.newClient();
client.register(SignatureClientRequestFilter.class);
client.register(SignatureWriterInterceptor.class);

One thing is on the roadmap is to integrate it with the MicroProfile Rest Client way of working so that you can register the Signatures Feature and you’re done.

The initial code can be found on Github.

Conclusion

With the Http Signatures specification, you can add a header which can be used to verify if the message is changed or not. This is a nice non-intrusive way of having an End-To-End protection implement which can complement the SSL features to be sure that no intermediary party changes the message.

Have fun.

Categories
Security

Key derivation functions

Introduction

Although initially intended for creating better secret keys, Key derivation functions are probably better for storing hashed passwords. This post tells you more about what a Key derivation function does, how you can use it for storing hashed passwords and how you can use it in the Octopus framework.

What is it?

For the symmetric encryption you need the secret key, an array of bytes basically. A password can also be converted to a byte array so a password can be used. Then the key doesn’t need to be stored and when the user types in the password, the message can be encrypted or decrypted.

But of course, passwords are not a really good candidate as the key for an encryption.
– passwords consist of characters which have all more or less the same bit pattern. So the ‘random’ spread of bits through-out the byte array is not good.
– password are mostly short, so your key is probably too small.

So that is where the Key derivation functions come into the picture. They take a password or passphrase (a sentence used as a password) and can generate a byte array out of this. It has the following characteristics
– It needs a salt, a byte array, to be able to do his work. So salting is mandatory.
– When the same password/passphrase and salt are presented to the Key Derivation function, it results in the same byte array outcome (so it is deterministic, no random outcome)
– It is a one-way procedure. So from the output buts, the original password / passphrase can’t be reconstructed.
– The output, the number of bits generated, is configurable (important because key sizes for encryption must have some minimum lengths)
– The functions are intentionally slow to counter any brute-force attack. And iterations can be applied to make it even more difficult.

You can read more background info on this wiki page

for hashed passwords

In the previous section, you can read why those Key derivation functions are very good for generating a secret key (for symmetric encryption). The properties are designed specifically for that purpose.

But very rapidly, they saw an alternative use for these functions, create hashed passwords. The reason why are easy to see
– When offered the same input, the same output is generated
– The input can’t be reconstructed from the output.

But they have a few other properties which make it a better candidate for storing passwords then the classic hashing functions.
– The computation requires a salt, so generating an output byte array is not possible without it. This in contrast to a classic hash algorithm where we add the salt to the password to overcome the use of rainbow tables.
– The computation of Key Derivation functions are slow, those of hash algorithms are fast. And performing the hashing function multiple times is not a good idea because of the increase in collisions.

Algorithms

So we are now at the point that we can say that Key Derivation functions are better for storing hashed passwords. So how can we get started with it in our Java programs?

There are different algorithms developed (just like there are different hash algorithms) like argon2, sCript, bCript or PBKDF2.
Only one is available by default on the JVM, PBKDF2, although most people say argon2 is the best algorithm. So let us see how we can use that one on the JVM.

Since Key derivation functions are something completely different then hash algorithms, these aren’t available from java.security.MessageDigest class. But there is another standard java class available which give you an instance of the algorithm, javax.crypto.SecretKeyFactory

The following snippets generate the BASE64 encoded output for a certain password. It also generates a salt value.

String password = "atbash";

String keyAlgorithmName = "PBKDF2WithHmacSHA256";
SecretKeyFactory keyFactory;
keyFactory = SecretKeyFactory.getInstance(keyAlgorithmName);

char[] chars = password.toCharArray();

byte[] salt = new byte[32];
new SecureRandom().nextBytes(salt);

int hashIterations = 1024;

int keySizeBytes = 32;

byte[] encoded = keyFactory.generateSecret(
        new PBEKeySpec(chars, salt, hashIterations, keySizeBytes * 8)).getEncoded();

String hashed = Base64.getEncoder().encodeToString(encoded);

System.out.println(hashed);

So using them us quite easily.

Octopus support

Also within Octopus, there is support added for the PBKDF2 algorithm within version 0.9.7.1. And since the way you use it is so similar then classic Hash algorithms, the only thing you need to do is set the algorithm name in the configuration file.

hashAlgorithmName=PBKDF2WithHmacSHA256

See also the chapter in the Octopus Cookbook

Conclusion

Key derivation functions are designed for creating a key which can be used in Symmetric encryption algorithms based on a password.

But it turns out that they are also very well suited to stored hashed passwords. So try them out and use them in your next project with or without the Octopus security framework.

Have fun.

 

This website uses cookies. By continuing to use this site, you accept our use of cookies.  Learn more