//The data to be checksumed.
final var data = "Hello";
final MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
try (DigestInputStream dis = new DigestInputStream(
//This can be replaced with FileInputStream.
//The data just needs to be a File path.
new ByteArrayInputStream(data.getBytes())
, md)) {
final var buffer = new byte[4096];
int bytesRead;
while ((bytesRead = dis.read(buffer)) != -1) {
// Read the data into the buffer to update the digest
md.update(buffer, 0, bytesRead);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
final var digest = md.digest();
final var sb = new StringBuilder();
for (final byte b : digest) {
sb.append(String.format("%02x", b & 0xff));
}
System.out.println("Checksum: " + sb.toString());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
Retrieving the X509 Certificate Information in Java
//This will hold the target leaf certificate.
Optional<Certificate> cert;
try {
//Must hold the target URL.
final var targetUrl = "https://www.google.com";
final var httpsURL = new URL(targetUrl);
final var connection = (HttpsURLConnection) httpsURL.openConnection();
connection.connect();
//Retrieve the first certificate. This is normally the leaf certificate.
cert = Arrays.stream(connection.getServerCertificates()).findFirst();
} catch (IOException e) {
throw new RuntimeException(e);
}
cert.ifPresent(cer -> {
//Check if the instance of certificate is of type of X509Certificate.
if(cer instanceof final X509Certificate x509) {
System.out.println("Subject: " + x509.getSubjectX500Principal().getName());
System.out.println("From: " + x509.getNotBefore());
System.out.println("To: " + x509.getNotAfter());
System.out.println("Duration: " + ChronoUnit.DAYS.between(LocalDate.now(), LocalDate.ofInstant(x509.getNotAfter().toInstant(),ZoneId.systemDefault())));
System.out.println("\n");
}
});
Showing JVM Flags and Values
Flags
Flag | Description |
---|---|
UnlockDiagnosticVMOptions | Unlocks the options intended for diagnosing the JVM. By default, this option is disabled and diagnostic options are not available. |
PrintFlagsFinal | Shows the final flag values that JVM uses. |
Usage
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version
The usage of version argument is used to show the java version information instead of the help information.
Basic Docker Container Management
Run Command
Run a command in a new container
Display the help information using the following command:
docker container run --help
Example of creating a container for the swaggerapi/swagger-editor image with the name swagger-editor.
docker container run -it -p 80:8080 --name swagger-editor swaggerapi/swagger-editor
Since the -it options were used, you can stop the container using CTRL+C.
Access the swagger-editor using the following address:
This command can only be used for creating a container based on an image. Once the container was created you can start it using the start command. If you are not sure about the name of the container find it using the list command.
It is better to have a name for the container so that you know what to start (or stop or delete).
List Command
List containers.
Display the help information using the following command:
docker container ls --help
Example of displaying all the containers.
docker container ls --all
Start Command
Start one or more stopped containers
Display the help information using the following command:
docker container start --help
Example of starting the container created from the run command section.
docker container start swagger-editor
Stop Command
Stop one or more running containers
Display the help information using the following command:
docker container stop --help
Example of stopping the container created from the run command section.
docker container stop swagger-editor
Delete Command
Remove one or more containers
Display the help information using the following command:
docker container rm --help
Example of deleting the container created from the run command section.
docker container rm swagger-editor
Filter-out Docker Legacy Commands
Set the following environment variable to 1:
DOCKER_HIDE_LEGACY_COMMANDS
Windows example
set DOCKER_HIDE_LEGACY_COMMANDS=1
To make this persist in windows, add this to windows environment variables.
Gradle Daemon to Use UTF-8 Encoding
Add the following entry to gradle.properties:
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
Container Aware JVM Parameters for Heap Memory
JVM Parameters
Modify the java heap memory based on container memory using one of the following JVM parameters:
Parameter | Description |
---|---|
-XX:InitialRAMPercentage | The initial size of the heap based on the total container memory. |
-XX:MinRAMPercentage | The maximum heap size based on the size of the JVM running on small heap. The small is heap is of approximately 125MB. |
-XX:MaxRAMPercentage | The maximum heap size based on the size of the JVM running on greater than small heap. |
Displaying the Current Value
Use the following command the display the current value of the container aware JVM Parameters using the following:
docker container run -it --rm openjdk:17.0.2-slim java -XX:+PrintFlagsFinal -version | grep -E ".*RAMPercentage"
Expect to see something like the following:
double InitialRAMPercentage = 1.562500
{product} {default}
double MaxRAMPercentage = 25.000000
{product} {default}
double MinRAMPercentage = 50.000000
{product} {default}
Checking the Current Memory and CPU using JShell
-
Open a jshell in the container using the following:
docker container run -it --rm openjdk:17.0.2-slim jshell
-
Paste the following command in jshell:
var rt = Runtime.getRuntime(); System.out.printf("Heap size: %dMB%nMaximum size of heap: %dMB%nAvailable processors: %d%n", rt.totalMemory()/1024/1024, rt.maxMemory()/1024/1024, rt.availableProcessors());
-
Press enter.
Expect to see something similar to the following:
rt ==> java.lang.Runtime@1d81eb93 Heap size: 252MB Maximum size of heap: 3966MB Available processors: 16 $2 ==> java.io.PrintStream@34c45dca
-
Type the following in jshell and press enter.
/exit
Allocating a Memory and CPU to a Container
-
Execute the following command to allocate 100mb of memory and 1 CPU to the container:
docker container run -it --rm -m 100m --cpus=1 openjdk:17.0.2-slim jshell
-
Paste the following command in jshell:
var rt = Runtime.getRuntime(); System.out.printf("Heap size: %dMB%nMaximum size of heap: %dMB%nAvailable processors: %d%n", rt.totalMemory()/1024/1024, rt.maxMemory()/1024/1024, rt.availableProcessors());
-
Press enter.
Expect to see something similar to the following:
rt ==> java.lang.Runtime@6659c656 Heap size: 7MB Maximum size of heap: 48MB Available processors: 1 $2 ==> java.io.PrintStream@2d8e6db6
Notice the following:
Value Heap size 7MB Maximum size of heap 48MB Available processors 1 -
Type the following in jshell and press enter.
/exit
Modifying the Allocated Maximum Size of Heap Memory
-
Execute the following command to allocate 100mb of memory and 1 CPU to the container:
docker container run -it --rm -m 100m --cpus=1 openjdk:17.0.2-slim jshell -R-XX:MinRAMPercentage=80
-
Paste the following command in jshell:
var rt = Runtime.getRuntime(); System.out.printf("Heap size: %dMB%nMaximum size of heap: %dMB%nAvailable processors: %d%n", rt.totalMemory()/1024/1024, rt.maxMemory()/1024/1024, rt.availableProcessors());
-
Press enter.
Expect to see something similar to the following:
rt ==> java.lang.Runtime@6659c656 Heap size: 7MB Maximum size of heap: 77MB Available processors: 1 $2 ==> java.io.PrintStream@2d8e6db6
Notice the Maximum size of heap become 77MB. This is because of the JVM argument -XX:MinRAMPercentage=80 passed in jshell as:
-R-XX:MinRAMPercentage=80
We use the --XX:MinRAMPercentage=80 because the memory allocated is a small heap.
-
Type the following in jshell and press enter.
/exit
The Resource Owner Password Credential (ROPC) Grant Type
The resource owner password credential grant type is designed as a stop-gap for legacy applications. Should only be used temporarily until the migration of the application to OAUTH is complete. This grant type should never be used anymore. This type can request for offline_access scope (i.e. to request for refresh token).
-
Use the token end point to do post request for the access token with the following headers:
Content-Type = application/x-www-form-urlencoded
And with the following form data:
grant_type = password client_id = the one used from step 1. client_secret =
username = password = scope = (Optional) what permision wanted. If not specified, default permission will be given. state = (Optional) value to echo to us. Expected Response
{ "access_token" : <ACCESS_TOKEN>, "token_type" : "Bearer", "expires_in" : 3600, "scope" : <The scope allowed by the server> }
-
Call the API with the authorization header like the following syntax:
Bearer <ACCESS_TOKEN>
Related Post
KEYCLOAK – JWT GENERATION – PASSWORD GRANT TYPE
Keycloak – Realm – OpenID Configuration
The open id configuration exposes some information like the following:
- Authorization endpoint
- Token endpoint
- Supported grant types
- Supported response types
- Supported response modes
- Supported claims
- Supported scopes
Use the following address syntax to find-out the OpenID configuration:
<KEYCLOAK_ADDRESS>/realms/<TARGET_REALM>/.well-known/openid-configuration
Example
Given
Token | Value |
---|---|
KEYCLOAK_ADDRESS | http://localhost:8080 |
TARGET_REALM | test |
The OpenID configuration would be:
http://localhost:8080/realms/testrealm/.well-known/openid-configuration
Keycloak – JWT Generation – Password Grant Type
Pre-requisite
- Keycloak 19^1
Creating a New Client and User
-
Sign in to keycloak admin console using the following address:
Must know a valid credential.
-
Switch or create a realm that is NOT a master realm (i.e. leave the master realms for keycloak usage only), like the following (i.e. jwtrealm):
-
Create a new client as follows:
-
Ensure that OpenID Connect is the Client type.
-
Provide a Client ID (e.g. jwtclient).
-
Click the Next button.
-
Enable the Client authentication.
-
In the Authentication flow, unselect the standard flow.
-
Click the Save button.
-
-
Create a new user as follows:
-
Fill-in the username field (e.g. testuser).
-
Click the Create button.
-
Click the Credentials tab.
-
Click the Set password button.
-
Fill-in the Password field.
-
Fill-in the Password confirmation field.
-
Turn-off temporary.
-
Click the Save button.
-
Click the Save password button.
-
Using Postman for Testing
-
Create a post request to the following address format:
http://localhost:8080/realms/<TARGET_REALM>/protocol/openid-connect/token
Example
Using the jwtrealm as the TARGET_REALM (i.e. configured in the previous section).
http://localhost:8080/realms/jwtrealm/protocol/openid-connect/token
-
Click the Body tab.
-
Select x-www-form-url-encoded.
-
Add the following entries:
Key Value Comment client_id jwtclient This is the client configured earlier. grant_type password This is for direct access grant type. client_secret <Client secret> This can be found in the jwtclient (i.e. configured earlier) client credentials tab. scope openid The openid scope is required; to indicate that the application intends to use OIDC to verify the user's identity. username testuser This is the user configured earlier. password <password> This is the password for the user that is configured earlier. -
Click the Send button.
Success Output
The success output is in the following format.
{
"access_token": "The access token.",
"expires_in": "Access token expiration.",
"refresh_expires_in": "Refresh token expiration",
"refresh_token": "The refresh token.",
"token_type": "Bearer",
"id_token": "The ID token.",
"not-before-policy": 0,
"session_state": "The session state.",
"scope": "openid profile email"
}
You paste the encoded token to the following website to decode its content:
Invalid Credential Output
{
"error": "invalid_grant",
"error_description": "Invalid user credentials"
}
Related Post
THE RESOURCE OWNER PASSWORD CREDENTIAL (ROPC) GRANT TYPE