Custom CA Certificates (Java)

Overview

You can configure Java-based container images (openjdk, etc.) to trust custom Certificate Authority (CA) certificates for TLS connections. Java uses its own truststore format (JKS/PKCS12) rather than PEM files.

Java keystores require a password for integrity checking. The system truststore uses the standard password changeit, which is a well-known default used for tamper detection rather than confidentiality.

Volume Mount Approach

Create a custom truststore using the keytool command. You can either trust only your specific CAs (for internal-only applications) or merge with the default public CAs (typical for applications connecting to both internal and external services).

CERTDIR=$(mktemp -d)  # or use a persistent directory for repeated runs

# OPTIONAL: Copy the default Java truststore to merge with public CAs
# Skip this step to trust ONLY your custom CAs
podman run --rm -v "${CERTDIR}:/work:Z" \
  quay.io/hummingbird/openjdk:latest \
  cp /etc/pki/ca-trust/extracted/java/cacerts /work/cacerts

# Import your custom CA
podman run --rm -v "${CERTDIR}:/work:Z" \
  quay.io/hummingbird/openjdk:latest \
  keytool -import -noprompt -trustcacerts -alias my-custom-ca \
    -file /work/my-ca.crt \
    -keystore /work/cacerts \
    -storepass changeit -storetype JKS

Podman Example

Mount the custom truststore to the system location:

podman run --rm \
  -v "${CERTDIR}/cacerts:/etc/pki/ca-trust/extracted/java/cacerts:ro,Z" \
  quay.io/hummingbird/openjdk:latest \
  java -jar myapp.jar

Kubernetes Example

In Kubernetes, use an init container to create the truststore, then share it via an emptyDir volume:

apiVersion: v1
kind: ConfigMap
metadata:
  name: custom-ca
data:
  ca.crt: |
    -----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----
---
apiVersion: v1
kind: Pod
spec:
  initContainers:
  - name: prepare-truststore
    image: quay.io/hummingbird/openjdk:latest
    command: ["sh", "-c"]
    args:
    - |
      # OPTIONAL: Copy the default truststore to merge with public CAs
      # Skip this line to trust ONLY your custom CAs
      cp /etc/pki/ca-trust/extracted/java/cacerts /truststore/cacerts
      chmod 666 /truststore/cacerts
      keytool -import -noprompt -trustcacerts -alias custom-ca \
        -file /ca/ca.crt -keystore /truststore/cacerts \
        -storepass changeit -storetype JKS
    volumeMounts:
    - name: custom-ca
      mountPath: /ca
    - name: truststore
      mountPath: /truststore
  containers:
  - name: app
    image: quay.io/hummingbird/openjdk:latest
    volumeMounts:
    - name: truststore
      mountPath: /etc/pki/ca-trust/extracted/java
      readOnly: true
  volumes:
  - name: custom-ca
    configMap:
      name: custom-ca
  - name: truststore
    emptyDir: {}

For the trust-only-specific-CAs approach, omit the cp command and create the keystore directly with keytool -import.

Derived Image Approach

Use this approach when it’s more practical to bake the certificates into the image instead of generating the truststore on each container startup. This creates a new image with a merged trust store with the trust anchor command.

Create a multi-stage Containerfile with your ca.crt in the build context. This example derives from the openjdk image:

FROM quay.io/hummingbird/openjdk:latest-builder AS builder

USER root
COPY ca.crt /tmp/
RUN trust anchor /tmp/ca.crt

FROM quay.io/hummingbird/openjdk:latest
COPY --from=builder /etc/pki /etc/pki

Build and use your custom image:

podman build -t my-openjdk-with-ca .
podman run --rm my-openjdk-with-ca java -jar myapp.jar