- Application developers migrating Java services from standard OpenJDK images
- DevOps and platform teams owning Docker and Kubernetes rollout
- Compliance and security teams reviewing migration controls
Overview
Use Minimus images to build a FIPS compliant Java app using FIPS 140-3 validated images. These images are configured with FIPS-validated cryptographic providers and enforce strict FIPS compliance at runtime to ensure cryptographic operations are compliant with Federal Information Processing Standards. To be FIPS-compliant, every cryptographic operation (encryption, hashing, key generation, TLS) must go through a CMVP-certified provider.Multi-stage build technique
Multi-stage builds are great for keeping the build and runtime environments separate and include only the compiled .class in the final image. The recommended process for all workloads is to build with Maven/Gradle/OpenJDK-FIPS and use OpenJRE-FIPS for the runtime stage. Java development in Minimus typically employs multi-stage Dockerfiles that separate the build/compile and runtime stages by using complementary images:- OpenJDK (Open Java Development Kit) is an open-source implementation of the Java Platform, Standard Edition (Java SE). Use it to compile, package, run unit tests, and any step that needs Java build tooling. It includes a Java compiler
javacand the full JDK. - OpenJRE (open-source Java Runtime Environment) is used to run Java applications built with OpenJDK. OpenJRE includes the JVM (Java Virtual Machine) and core libraries needed to run Java applications, without the compiler
javac. OpenJRE has a smaller footprint than the OpenJDK image and only includes what is needed to run a compiled Java application.
See also the Minimus tutorial for Java and Minimus tips for multi-stage builds.
Components
| Component | Purpose |
|---|---|
reg.mini.dev/mavenreg.mini.dev/gradle | Build stage images |
reg.mini.dev/openjdk-fips | Minimal OpenJDK image, full JDK toolset, does not include Maven or Gradle |
reg.mini.dev/openjre-fips | Production runtime stage, leaner JRE-only image |
| Application JAR | Build artifact copied from the build stage into the runtime stage |
| Optional BCFKS keystore | Required keystore format for private keys in FIPS deployments |
Environment variables
The images are pre-set with the following environment variables:| Variable | Value |
|---|---|
CLASSPATH | Includes FIPS libraries from /usr/share/fips-libs/* |
JAVA_FIPS_CLASSPATH | Explicit FIPS classpath reference |
JAVA_HOME | Points to the default JVM installation |
JDK_JAVA_OPTIONS | --add-exports=java.base/sun.security.internal.spec=ALL-UNNAMED --add-exports=java.base/sun.security.provider=ALL-UNNAMED -Djavax.net.ssl.trustStoreType=FIPS |
The critical java -jar problem
java -jar ignores CLASSPATH (and any -cp/--class-path you might pass), so the FIPS provider jars do not get loaded unless they are on the bootstrap classpath. Regardless of whether -Xbootclasspath/a is supplied directly in your ENTRYPOINT or injected via JDK_JAVA_OPTIONS, the CCJ provider jars must be appended to the bootstrap classpath; otherwise the JVM will have no available FIPS ciphers. The symptoms are either an empty cipher list or a NoSuchAlgorithmException at startup.
The correct way to launch applications from a FIPS image is to use -Xbootclasspath/a to append the FIPS jars directly to the bootstrap classloader, which is not bypassed by java -jar:
/usr/share/fips-libs/* loads all jars in that directory. Do not hardcode specific jar filenames, as the version numbers change as the image updates. Pinning the version numbers will cause silent classpath failures.
FIPS approved algorithms
FIPS approved-only mode (com.safelogic.cryptocomply.fips.approved_only=true) enforces a hard algorithm blocklist. Your application will throw a NoSuchAlgorithmException or GeneralSecurityException at runtime - not at compile time - if it calls any of the blocked algorithms.
Below is a table showing the recommended migration paths:
| Algorithms blocked by FIPS | FIPS-approved replacement algorithm |
|---|---|
| MD5 (for any security purpose) | SHA-256 or SHA-3 |
| SHA-1 signatures | SHA-256 or stronger |
| DES / 3DES | AES-128 or AES-256 |
| RC4 | AES-GCM |
| TLS 1.0 / TLS 1.1 | TLS 1.2 or TLS 1.3 |
| RSA or DH keys under 2048 bits | RSA-2048 minimum, RSA-3072 preferred |
| PKCS#12 for private key storage | BCFKS keystore format |
"MD5", "SHA1", "DES", "RC4", "TLSv1", and "PKCS12" in any getInstance() or KeyStore.getInstance() calls.
How to deploy Java with the Minimus OpenJRE-FIPS image
Prerequisites
- Docker or Podman available locally
- Token to pull images from the Minimus image registry
- Existing Java project (Maven or Gradle)
- A working test environment for smoke tests and crypto-related checks
- Host with FIPS-enabled kernel as listed in the CMVP certificate
Step 1: Pre-flight modifications
Update your application to use only FIPS-approved algorithms
The first step is to audit your application for non-FIPS algorithm usage. Before changing your Dockerfile, scan your codebase for algorithm strings that FIPS will reject at runtime.Common offenders are
MD5, SHA1, SHA-1, DES, RC4, TLSv1, TLS1.0, TLS1.1, and PKCS12 used as a keystore type for private keys.Replace any incompatible algorithms found with FIPS-approved equivalents and ensure TLS configuration specifies a minimum of TLSv1.2. See the list of approved algorithmsRun your existing test suite before proceeding.Update your Dockerfile
Update your Dockerfile to use Minimus images:
- Use a build image that includes your build tooling (Maven or Gradle). We need to use Maven or Gradle because the Minimus OpenJDK-FIPS image does not include Maven or Gradle by default.
- Copy the produced JAR into the OpenJRE-FIPS image for the runtime stage.
- Make sure the ENTRYPOINT includes
-Xbootclasspath/a:/usr/share/fips-libs/*to load the CCJ FIPS provider. Without this flag,java -jarbypasses the image’s pre-setCLASSPATH(and any-cp/--class-pathyou might pass), so the FIPS provider jars are never loaded into the bootstrap classloader and the JVM will have no available FIPS ciphers. - Use a wildcard such as
/usr/share/fips-libs/*to load all CryptoComply jars in that directory. Avoid hardcoding specific jar filenames such asccj-4.0.0-fips.jarsince the version numbers change as the image updates. Pinning the version numbers will cause classpath failures silently and should be avoided. - Set a Main-Class to avoid errors. If your build produces a non-executable JAR (no
Main-Classmanifest entry),java -jar /app/app.jarwill fail with the error:no main manifest attribute. Ways to fix this issue:- Configure Maven or Gradle to produce an executable JAR (set
Main-Class) - Run the app with an explicit main class, e.g.
java -cp /app/app.jar com.example.App.
- Configure Maven or Gradle to produce an executable JAR (set
Step 2: Deploy your Java project
Validate that image roles are correct
Confirm the build image (Confirm the runtime image (If any step in your Dockerfile requires
openjdk-fips) has compiler access:Confirm
openjre-fips) is JRE-only:Confirm
javac or other JDK tools, it belongs in the openjdk-fips build stage. The openjre-fips runtime stage should only execute the already-compiled artifact.Step 3: Verify your app
Verify FIPS provider loading
Save the following code as Compile and run:Expected output:If TLS connections return an empty cipher list or throw
TestFIPS.java . We will run it against the image to confirm the CCJ and BCJSSE providers are loaded at the correct positions and that non-FIPS algorithms are blocked:TestFIPS.java
Run
Expected
NoSuchAlgorithmException, the CCJ provider is not being registered. Check that -Xbootclasspath/a:/usr/share/fips-libs/* is present in your ENTRYPOINT.Run smoke tests in the new image
Validate the following application paths:
- Service startup
- Health endpoints
- TLS client and server connections
- Authentication, token signing, and password hashing paths
- Any code paths that invoke cryptographic operations directly
Step 4: Roll out to Kubernetes
Create a Minimus registry pull secret
Create a pull secret for the Minimus registry (first update the command with your Minimus token and the relevant namespace):
Create
Update your deployment YAML and deploy
Update your Deployment to reference Apply the rollout and verify:
openjre-fips as the runtime image and add the imagePullSecrets:Apply
Handle keystore requirements
FIPS mode restricts the keystore formats allowed for private key storage. For example, FIPS mode bans the standard
If your application currently loads a
JKS and PKCS12 formats. The Bouncy Castle FIPS KeyStore format (BCFKS) is required instead.| Use case | Allowed formats |
|---|---|
| Storing private keys | bcfks only |
| Truststores (public CA certs only) | jks, pkcs12, or bcfks |
If your team needs a complete keystore and certificate generation workflow — including
keytool commands for BCFKS keystore and truststore creation, CA generation, and certificate signing — see the Keycloak FIPS Tutorial. The keytool patterns shown there apply directly to any Java application using Minimus FIPS images, not just Keycloak..jks or .p12 file containing a private key at startup and passes it to an SSLContext, that will throw a KeyStoreException at runtime in FIPS mode. The keystore must be regenerated in bcfks format before deploying.Troubleshooting
| Issue | Likely cause | Action |
|---|---|---|
| Empty cipher list at startup | CCJ provider not registered — java -jar bypassed CLASSPATH | Add -Xbootclasspath/a:/usr/share/fips-libs/* to ENTRYPOINT |
| NoSuchAlgorithmException: RSA KeyFactory | FIPS provider not loaded | Add -Xbootclasspath/a:/usr/share/fips-libs/* to ENTRYPOINT |
| Runtime algorithm errors | Legacy non-approved algorithm in use | Replace with FIPS-approved algorithms and rerun tests |
| App works in build stage but not runtime | Build-only tools expected at runtime | Move compile steps to the openjdk-fips stage only |
| Unexpected Java option behavior | Default Java options overwritten | Extend JDK_JAVA_OPTIONS using ${JDK_JAVA_OPTIONS} |
| Keystore loading errors | Private-key store format mismatch | Convert private key keystores to BCFKS |
| Password rejected at keystore creation | Password under 14 characters | Use a password of 16–24 characters (minimum 112 bits) |
| Cluster pull failures | Missing or invalid registry secret | Recreate pull secret and verify namespace binding |