QCecuring - Enterprise Security Solutions

Java cacerts Trust Store: Complete Management Guide

SSL/TLS 15 May, 2026 · 07 Mins read

The definitive reference for Java's cacerts trust store — locate it across JDK versions, list trusted CAs, import and remove certificates with keytool, configure custom trust stores, handle Docker containers, and troubleshoot PKIX path building failures.


Every Java application that makes an HTTPS connection relies on the cacerts trust store. When a Java client connects to a server, it validates the server’s certificate chain against the CAs in cacerts. If the issuing CA isn’t there — or if an intermediate is missing — you get the dreaded PKIX path building failed error.

This guide is the definitive reference for Java trust store operations: finding cacerts across JDK versions, listing and managing trusted CAs, importing corporate and internal certificates, using custom trust stores, programmatic access, and handling the unique challenges of containerized Java applications.


What is cacerts?

The cacerts file is a Java KeyStore (JKS or PKCS12) that contains trusted root CA certificates. When Java’s TLS implementation (JSSE) validates a server certificate, it builds a chain from the server cert up to a root CA. If that root CA exists in cacerts, the connection is trusted.

Flowchart showing top-down process flow

Key facts:

  • Format: JKS (Java 8 and earlier) or PKCS12 (Java 9+, default since Java 9)
  • Default password: changeit (yes, for every JDK installation)
  • Contents: ~90-140 trusted root CA certificates (varies by JDK vendor and version)
  • Scope: JVM-wide — all applications using that JDK share the same cacerts

Locating cacerts Across JDK Versions

Java 8 and Earlier

# Standard location
$JAVA_HOME/jre/lib/security/cacerts

# Common paths
/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts      # Ubuntu/Debian
/usr/lib/jvm/java-1.8.0-openjdk/jre/lib/security/cacerts         # RHEL/CentOS
/Library/Java/JavaVirtualMachines/jdk1.8.0_xxx.jdk/Contents/Home/jre/lib/security/cacerts  # macOS
C:\Program Files\Java\jdk1.8.0_xxx\jre\lib\security\cacerts      # Windows

Java 9+ (Including 11, 17, 21)

Java 9 removed the separate jre/ directory:

# Standard location
$JAVA_HOME/lib/security/cacerts

# Common paths
/usr/lib/jvm/java-17-openjdk-amd64/lib/security/cacerts          # Ubuntu/Debian
/usr/lib/jvm/java-17-openjdk/lib/security/cacerts                 # RHEL/CentOS
/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/lib/security/cacerts  # macOS
C:\Program Files\Java\jdk-17\lib\security\cacerts                 # Windows

Find It Programmatically

# Works on any system with java in PATH
java -XshowSettings:all 2>&1 | grep -i "java.home"

# Then append the path
CACERTS_PATH="$(java -XshowSettings:properties 2>&1 | grep java.home | awk '{print $3}')/lib/security/cacerts"
echo $CACERTS_PATH

Verify the File

keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit | head -20

Listing Trusted CAs

List All Certificates

keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit

This outputs alias names and fingerprints. For detailed information:

# Verbose — shows full certificate details
keytool -list -v -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit

# Count total certificates
keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit | grep -c "trustedCertEntry"

Search for a Specific CA

# Find by alias
keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -alias "digicertglobalrootg2"

# Search by issuer name
keytool -list -v -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit | grep -B 5 "DigiCert"

Export a CA Certificate

keytool -exportcert -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -alias "digicertglobalrootg2" -file digicert-root-g2.crt -rfc

Importing Certificates

Import a Root or Intermediate CA

keytool -importcert \
  -keystore $JAVA_HOME/lib/security/cacerts \
  -storepass changeit \
  -alias "my-internal-ca" \
  -file /path/to/internal-ca.crt \
  -noprompt

Flags explained:

  • -alias — Unique name for this entry (lowercase, no spaces recommended)
  • -file — Path to the certificate file (PEM or DER format)
  • -noprompt — Skip the “Trust this certificate?” confirmation
  • -storepass changeit — Default password

Import from a Running Server

If you don’t have the certificate file, extract it from the server:

# Download the server's certificate chain
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null | openssl x509 -outform PEM > server-cert.pem

# Import it
keytool -importcert -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -alias "api-example-com" -file server-cert.pem -noprompt

Import a Full Chain

For chains with multiple certificates, import each one separately:

# Split the chain file into individual certs
csplit -f cert- -b '%02d.pem' fullchain.pem '/-----BEGIN CERTIFICATE-----/' '{*}'

# Import each (skip the first empty file if present)
for cert in cert-*.pem; do
  if [ -s "$cert" ]; then
    alias=$(openssl x509 -noout -subject -in "$cert" | sed 's/.*CN = //' | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
    keytool -importcert -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -alias "$alias" -file "$cert" -noprompt
    echo "Imported: $alias"
  fi
done

Removing Certificates

Delete a Trusted CA

keytool -delete -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -alias "my-internal-ca"

Verify Removal

keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -alias "my-internal-ca"
# Should output: keytool error: java.lang.Exception: Alias <my-internal-ca> does not exist

Updating cacerts After JDK Upgrades

When you upgrade the JDK, you get a fresh cacerts file. Any custom certificates you imported into the old JDK’s cacerts are lost.

Strategy 1: Re-Import After Upgrade

Maintain a directory of custom certificates and a script to import them:

#!/bin/bash
# /opt/scripts/import-custom-certs.sh
CACERTS="$JAVA_HOME/lib/security/cacerts"
CERT_DIR="/opt/custom-certs"
STOREPASS="changeit"

for cert in "$CERT_DIR"/*.crt; do
  alias=$(basename "$cert" .crt)
  keytool -importcert -keystore "$CACERTS" -storepass "$STOREPASS" -alias "$alias" -file "$cert" -noprompt 2>/dev/null
  if [ $? -eq 0 ]; then
    echo "Imported: $alias"
  else
    echo "Skipped (already exists or error): $alias"
  fi
done

Run this script after every JDK upgrade.

Instead of modifying cacerts, maintain a separate trust store:

# Create custom trust store from cacerts (copy the base)
cp $JAVA_HOME/lib/security/cacerts /opt/app/truststore.jks

# Add your custom certs
keytool -importcert -keystore /opt/app/truststore.jks -storepass changeit -alias "internal-ca" -file /opt/custom-certs/internal-ca.crt -noprompt

Then point your application to it (see next section).


Using Custom Trust Stores

JVM System Properties

java -Djavax.net.ssl.trustStore=/opt/app/truststore.jks \
     -Djavax.net.ssl.trustStorePassword=changeit \
     -Djavax.net.ssl.trustStoreType=PKCS12 \
     -jar myapp.jar

Environment Variable (for frameworks that support it)

Some frameworks read trust store configuration from environment variables:

export JAVA_OPTS="-Djavax.net.ssl.trustStore=/opt/app/truststore.jks -Djavax.net.ssl.trustStorePassword=changeit"

Spring Boot Configuration

# application.properties
server.ssl.trust-store=/opt/app/truststore.jks
server.ssl.trust-store-password=changeit
server.ssl.trust-store-type=PKCS12

Programmatic Access: SSLContext and TrustManagerFactory

For fine-grained control, configure trust programmatically:

Load a Custom Trust Store

import javax.net.ssl.*;
import java.io.FileInputStream;
import java.security.KeyStore;

public class CustomTrustStore {

    public static SSLContext createSSLContext(String trustStorePath, String password) throws Exception {
        // Load the trust store
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        try (FileInputStream fis = new FileInputStream(trustStorePath)) {
            trustStore.load(fis, password.toCharArray());
        }

        // Initialize TrustManagerFactory
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustStore);

        // Create SSLContext
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);

        return sslContext;
    }

    public static void main(String[] args) throws Exception {
        SSLContext ctx = createSSLContext("/opt/app/truststore.jks", "changeit");

        // Use with HttpsURLConnection
        HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());

        // Or use with HttpClient (Java 11+)
        // HttpClient client = HttpClient.newBuilder().sslContext(ctx).build();
    }
}

Combine Default + Custom Trust Stores

Sometimes you need both the default cacerts CAs and your custom CAs:

import javax.net.ssl.*;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Arrays;

public class CombinedTrustManager {

    public static SSLContext createCombinedSSLContext(String customTrustStorePath, String password) throws Exception {
        // Load default trust manager
        TrustManagerFactory defaultTmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        defaultTmf.init((KeyStore) null); // null = use default cacerts

        // Load custom trust store
        KeyStore customStore = KeyStore.getInstance(KeyStore.getDefaultType());
        try (FileInputStream fis = new FileInputStream(customTrustStorePath)) {
            customStore.load(fis, password.toCharArray());
        }
        TrustManagerFactory customTmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        customTmf.init(customStore);

        // Combine both trust managers
        X509TrustManager defaultTm = (X509TrustManager) defaultTmf.getTrustManagers()[0];
        X509TrustManager customTm = (X509TrustManager) customTmf.getTrustManagers()[0];

        X509TrustManager combinedTm = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
                try {
                    customTm.checkClientTrusted(chain, authType);
                } catch (Exception e) {
                    defaultTm.checkClientTrusted(chain, authType);
                }
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
                try {
                    customTm.checkServerTrusted(chain, authType);
                } catch (Exception e) {
                    defaultTm.checkServerTrusted(chain, authType);
                }
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                X509Certificate[] defaultIssuers = defaultTm.getAcceptedIssuers();
                X509Certificate[] customIssuers = customTm.getAcceptedIssuers();
                X509Certificate[] combined = Arrays.copyOf(defaultIssuers, defaultIssuers.length + customIssuers.length);
                System.arraycopy(customIssuers, 0, combined, defaultIssuers.length, customIssuers.length);
                return combined;
            }
        };

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{combinedTm}, null);
        return sslContext;
    }
}

Docker and Container Considerations

Containerized Java applications need special handling for trust stores because the base image’s cacerts may not include your internal CAs.

Dockerfile: Import Certificates at Build Time

FROM eclipse-temurin:17-jre-alpine

# Copy custom CA certificates
COPY certs/internal-ca.crt /usr/local/share/ca-certificates/internal-ca.crt
COPY certs/proxy-ca.crt /usr/local/share/ca-certificates/proxy-ca.crt

# Update system trust store (Alpine)
RUN update-ca-certificates

# Import into Java's cacerts
RUN keytool -importcert \
    -keystore $JAVA_HOME/lib/security/cacerts \
    -storepass changeit \
    -alias internal-ca \
    -file /usr/local/share/ca-certificates/internal-ca.crt \
    -noprompt

COPY app.jar /app/app.jar
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

Dockerfile: Use a Custom Trust Store

FROM eclipse-temurin:17-jre-alpine

# Copy pre-built trust store
COPY truststore.jks /opt/app/truststore.jks

COPY app.jar /app/app.jar
ENTRYPOINT ["java", \
    "-Djavax.net.ssl.trustStore=/opt/app/truststore.jks", \
    "-Djavax.net.ssl.trustStorePassword=changeit", \
    "-jar", "/app/app.jar"]

Kubernetes: Mount Certificates via ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: custom-ca-certs
data:
  internal-ca.crt: |
    -----BEGIN CERTIFICATE-----
    MIIDxTCCAq2gAwIBAgIQAqxcJmoLQ...
    -----END CERTIFICATE-----
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: java-app
spec:
  template:
    spec:
      initContainers:
        - name: import-certs
          image: eclipse-temurin:17-jre-alpine
          command: ['sh', '-c']
          args:
            - |
              cp $JAVA_HOME/lib/security/cacerts /shared/cacerts
              keytool -importcert -keystore /shared/cacerts -storepass changeit -alias internal-ca -file /certs/internal-ca.crt -noprompt
          volumeMounts:
            - name: ca-certs
              mountPath: /certs
            - name: shared-truststore
              mountPath: /shared
      containers:
        - name: app
          image: my-java-app:latest
          env:
            - name: JAVA_OPTS
              value: "-Djavax.net.ssl.trustStore=/truststore/cacerts -Djavax.net.ssl.trustStorePassword=changeit"
          volumeMounts:
            - name: shared-truststore
              mountPath: /truststore
      volumes:
        - name: ca-certs
          configMap:
            name: custom-ca-certs
        - name: shared-truststore
          emptyDir: {}

Corporate Proxy CA Import

Corporate proxies that perform TLS inspection inject their own CA certificate into the chain. Java applications behind these proxies fail with PKIX path building failed unless the proxy CA is in cacerts.

Find the Proxy CA Certificate

# Connect through the proxy and extract the CA
openssl s_client -connect google.com:443 -proxy proxy.corp.example.com:8080 -showcerts 2>/dev/null | openssl x509 -outform PEM > proxy-ca.pem

# Or export from your browser's certificate viewer
# Chrome: Settings → Privacy → Security → Manage certificates → Authorities

Import the Proxy CA

keytool -importcert \
  -keystore $JAVA_HOME/lib/security/cacerts \
  -storepass changeit \
  -alias "corporate-proxy-ca" \
  -file proxy-ca.pem \
  -noprompt

Gradle/Maven Behind Corporate Proxy

# Gradle
./gradlew build -Djavax.net.ssl.trustStore=/path/to/truststore.jks -Djavax.net.ssl.trustStorePassword=changeit

# Maven
mvn clean install -Djavax.net.ssl.trustStore=/path/to/truststore.jks -Djavax.net.ssl.trustStorePassword=changeit

Or set it globally in JAVA_TOOL_OPTIONS:

export JAVA_TOOL_OPTIONS="-Djavax.net.ssl.trustStore=/opt/truststore.jks -Djavax.net.ssl.trustStorePassword=changeit"

cacerts vs Application-Specific Trust Stores

Aspectcacerts (JDK-level)Application Trust Store
ScopeAll Java apps on this JDKSingle application
Location$JAVA_HOME/lib/security/cacertsCustom path
Survives JDK upgradeNoYes
Shared across appsYesNo
Security riskBroader — affects all appsIsolated
ManagementRequires JDK-level accessApp team manages
Best forSystem-wide CAs, corporate CAsApp-specific internal CAs

Recommendation: Use application-specific trust stores for production applications. Modify cacerts only for development environments or system-wide corporate CA requirements.


Changing the Default Password

While changeit is the universal default, you can change it:

keytool -storepasswd -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -new "newSecurePassword"

After changing the password, any application using -Djavax.net.ssl.trustStorePassword must be updated. Applications that rely on the default (no explicit password) will fail.


Debugging Trust Store Issues

Enable SSL Debug Logging

java -Djavax.net.ssl.debug=all -jar myapp.jar 2>&1 | tee ssl-debug.log

# More focused — just trust decisions
java -Djavax.net.ssl.debug=trustmanager -jar myapp.jar

Verify Which Trust Store Is Being Used

java -Djavax.net.ssl.debug=trustmanager -jar myapp.jar 2>&1 | grep "trustStore"

Test a Connection Against a Specific Trust Store

# Using keytool to verify a cert would be trusted
keytool -printcert -sslserver api.example.com:443 -keystore /opt/app/truststore.jks -storepass changeit

FAQ

Q: What happens if I delete all certificates from cacerts?

Every outbound HTTPS connection from that JDK will fail with PKIX path building failed. The JVM has no trusted CAs to validate against. To recover, copy a fresh cacerts from a new JDK installation or re-download it from your JDK vendor.

Q: Is the cacerts password (changeit) a security risk?

The password protects the integrity of the trust store (preventing unauthorized modifications), not confidentiality. Since cacerts contains only public CA certificates (no private keys), the password is more of a tamper-detection mechanism. In production, restrict file permissions (chmod 644) rather than relying on the password.

Q: Can I use a PKCS12 trust store instead of JKS?

Yes. Java 9+ defaults to PKCS12 format for new keystores. You can convert an existing JKS cacerts to PKCS12:

keytool -importkeystore -srckeystore cacerts -srcstoretype JKS -destkeystore cacerts.p12 -deststoretype PKCS12 -storepass changeit

Then reference it with -Djavax.net.ssl.trustStoreType=PKCS12.

Q: How do I handle cacerts in a CI/CD pipeline?

Store custom certificates in your secrets manager or artifact repository. Add a pipeline step that imports them before running tests:

# GitHub Actions example
- name: Import custom CA
  run: |
    echo "${{ secrets.INTERNAL_CA_CERT }}" > /tmp/internal-ca.crt
    keytool -importcert -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -alias internal-ca -file /tmp/internal-ca.crt -noprompt

Q: Why does my certificate work in the browser but not in Java?

Browsers maintain their own trust stores (separate from Java’s cacerts) and often cache intermediate certificates. Java is stricter — it requires the complete chain to be presented by the server or all intermediates to be in the trust store. Use openssl s_client -connect host:443 -showcerts to verify the server sends the full chain.

Q: Do I need to restart the JVM after importing a certificate?

Yes, for the default cacerts. The trust store is loaded once at JVM startup. Changes to cacerts require a JVM restart to take effect. For programmatic trust store loading (using TrustManagerFactory), you can reload without restarting by reinitializing the SSLContext.


Related Reading:

SSL Checker

Verify your server's certificate chain is trusted by Java clients — catch missing intermediates before they cause PKIX errors.

Check Certificate Chain

Related Insights

SSL/TLS

Fix 'The Certificate Chain Could Not Be Built to a Trusted Root Authority'

Fix the Windows certificate chain trust error. Covers missing root CA, intermediate certificate gaps, AIA/CDP issues, GPO trust distribution, and manual import — with certutil verification commands.

By Shivam sharma

15 May, 2026 · 06 Mins read

SSL/TLSTroubleshootingPKI

SSL/TLS

Fix 'Unable to Get Local Issuer Certificate' (OpenSSL, curl, Git, npm)

Fix the 'unable to get local issuer certificate' error in OpenSSL, curl, Git, npm, pip, and Docker. Covers missing CA bundles, corporate proxies, and trust store configuration for every platform.

By Sneha gupta

15 May, 2026 · 07 Mins read

SSL/TLSTroubleshootingDevOps

SSL/TLS

Fix 'Certificate Has Expired' Error: Emergency Response Guide

Emergency fix for expired SSL/TLS certificates causing production outages. Immediate diagnosis with openssl, emergency renewal via Certbot or commercial CA, and deployment to Nginx, Apache, IIS, and load balancers.

By Shivam sharma

15 May, 2026 · 05 Mins read

SSL/TLSTroubleshooting

Ready to Secure Your Enterprise?

Experience how our cryptographic solutions simplify, centralize, and automate identity management for your entire organization.

Stay ahead on cryptography & PKI

Get monthly insights on certificate management, post-quantum readiness, and enterprise security. No spam.

We respect your privacy. Unsubscribe anytime.