Use Java images with custom CA certificates

You can make your custom root Certificate Authority (CA) certificates available to Java images. Your application can then establish secure TLS connections to services that use certificates signed by your company’s CA.

Java uses the Java KeyStore (JKS) or Public-Key Cryptography Standards #12 (PKCS12) formats for key storage. However, you can import certificates in PEM files.

The key stores require a password for integrity checking. For the system truststore, the default password is changeit. This simple default password is intended for tamper detection and integrity verification rather than cryptographic confidentiality.

Add or replace custom CA certificates in Java images by using Podman

To add a custom Certificate Authority (CA) bundle to a Java image, you can use an OpenShift Container Platform ConfigMap. You can select whether you want to add your custom CA bundle to the existing truststore or create a new truststore that contains only your CA bundle.

Procedure

  1. Create a storage volume:
    $ podman volume create <volume_name>
  2. Enter the Podman user namespace shell:
    $ podman unshare
  3. Mount the volume:
    $ podman volume mount <volume_name>
    /home/<user>/.local/share/containers/storage/volumes/<volume_name>/_data
  4. Copy your custom certificate to the volume:
    # cp <path_to_certificate>.pem \
         /home/<user>/.local/share/containers/storage/volumes/<volume_name>/_data/
  5. Exit the user namespace shell:
    # exit
  6. If you want to add your custom CA bundle to the existing truststore, you must first export it:
    $ podman run \
        --rm \
        --volume <volume_name>:/work/:Z \
        --user 0 registry.access.redhat.com/hi/openjdk:latest \
          cp /etc/pki/ca-trust/extracted/java/cacerts /work/cacerts

    The command extracts the CA certificates from the Java truststore and stores it in the cacerts file on the storage volume.

    Skip this step if you want to trust only your custom CAs.

  7. Import the custom CA certificate into the Java truststore:
    $ podman run \
        --rm \
        --volume <volume_name>:/work:Z \
        registry.access.redhat.com/hi/openjdk:latest \
        keytool \
          -import \
          -noprompt \
          -trustcacerts \
          -alias <unique_name> \
          -file /work/<certificate_file_name>.pem \
          -keystore /work/cacerts \
          -storepass changeit \
          -storetype JKS

    The default password of the system truststore is changeit. The -alias <unique_name> option sets the name for the certificate in the truststore. It can be any value.

Results

  • Start an OpenJDK container and mount the storage volume with the truststore to the /etc/pki/ca-trust/extracted/java/cacerts file:
    # podman run --rm \
        --volume <volume_name>/cacerts:/etc/pki/ca-trust/extracted/java/cacerts:ro,Z \
        registry.access.redhat.com/hi/openjdk:latest \
          java -jar <application>.jar

Add or replace custom CA certificates in Java images on OpenShift Container Platform

Use an OpenShift Container Platform ConfigMap to add a custom Certificate Authority (CA) bundle to a Java image. You can select whether you want to add your custom CA bundle to the existing truststore or if you want to create a new truststore that contains only your CA bundle.

Before you begin

  • You have access to OpenShift Container Platform CLI.
  • You have the CA certificate bundle in PEM format.
  • You have the permissions to create ConfigMaps and modify Pod specifications in the target namespace.

Procedure

  1. Create a YAML configuration file that both defines the CA bundle and the volume mount necessary for your application to access it:
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: custom-ca
    data:
      ca.crt: |
        -----BEGIN CERTIFICATE-----
        <certificate>
        -----END CERTIFICATE-----
        <more_certificates_if_needed>
    ---
    apiVersion: v1
    kind: Pod
    spec:
      initContainers:
      - name: prepare-truststore
        image: quay.io/hummingbird/openjdk:latest
        command: ["sh", "-c"]
        args:
        - |
          # 1. Copy the default Java truststore to a writable location
          # Skip this step if you want to trust only your custom CAs
          cp /etc/pki/ca-trust/extracted/java/cacerts /truststore/cacerts
    
          chmod 666 /truststore/cacerts
    
          # 2. Import your file into the truststore (default password: changeit)
          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: registry.access.redhat.com/hi/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: {}
  2. Apply the configuration:
    $ oc apply -f <configuration_file>.yaml

Create a modified Java image to add custom root CA certificates

If you require that a Java image trusts both your custom Certificate Authorities (CAs) and the image’s built-in default CAs, create a modified image to add the custom certificates. This avoids the need to generate the truststore on each container startup.

Before you begin

  • The podman package is installed.

Procedure

  1. Create a project directory. For example:
    $ mkdir ~/project/
  2. Copy the custom CA certificate to your project directory:
    $ cp <path>/custom-root-ca.pem ~/project/
  3. Create a file named ~/project/Containerfile, that references the Red Hat Hardened Images in the FROM instructions. For example:
    # Build stage:
    FROM registry.access.redhat.com/hi/openjdk:latest-builder AS builder
    
    # Copy the certificate to the image
    COPY custom-root-ca.pem /tmp/
    
    # Temporarily switch to root to add the CA certificate to the truststore
    USER root
    RUN trust anchor /tmp/custom-root-ca.pem
    USER ${CONTAINER_DEFAULT_USER}
    
    
    # Runtime stage:
    # Copy the truststore from the builder image to the runtime image
    FROM registry.access.redhat.com/hi/openjdk:latest
    COPY --from=builder /etc/pki/ca-trust/extracted /etc/pki/ca-trust/extracted
  4. Build the custom image:
    $ podman build -t <image_name> ~/project/

    The -t <image_name> option specifies the name of the image after the build process.

  5. Optional: Display the list of images:
    $ podman image list
    REPOSITORY             TAG     IMAGE ID      CREATED        SIZE
    localhost/<image_name>  latest  03ca4c30326a  4 seconds ago  343 MB
  6. Create a container that uses the image. For example:
    $ podman run --rm <image_name>:latest ...