JKS Certificate Validation and Import Scripts

Preparation

Before executing any scripts, ensure the working directory contains the necessary decrypted artifacts.

Executing these scripts leaves decrypted cryptographic material in the file system. Ensure appropriate file permissions (chmod 600) and clean up the workspace after completion.

Required files: * Target Java KeyStore file (already decoded and extracted). * Plain text file containing the JKS password. * Source file containing one or multiple X.509 certificates in PEM format.

Formatting JKS Output for Human Readability

The default keytool -list -v output is unstructured and difficult to read. This script parses the verbose output and formats it into strict, readable blocks containing only the Alias, SHA-256 fingerprint, validity dates, and the Owner’s Distinguished Name.

JKS_FILE="truststore.jks"
PASS_FILE="pass.txt"

LC_ALL=C keytool -list -v -keystore "$JKS_FILE" -storepass "$(cat "$PASS_FILE")" | awk '
/^Alias name:/ {
    if (alias) {
        print "Alias : " alias
        print "SHA256: " fp
        print "Valid : " valid
        print "Owner : " owner
        print "------------------------------------------------------------"
    }
    sub(/^Alias name:[ \t]*/, "");
    alias=$0; fp="N/A"; valid="N/A"; owner="N/A"
}
/^Owner:/ { $1=""; owner=substr($0,2) }
/^Valid from:/ { valid=$0 }
/SHA256:/ || /SHA-256\):/ { fp=$NF }
END {
    if (alias) {
        print "Alias : " alias
        print "SHA256: " fp
        print "Valid : " valid
        print "Owner : " owner
        print "------------------------------------------------------------"
    }
}
'

This script uses brittle parsing (text scraping) of the CLI output. Changes in the Java Runtime Environment (JRE) version or OpenJDK distribution may alter the keytool output format, causing this script to return incomplete or N/A data.

Generating JKS State Dump

To avoid redundant disk I/O and repeated JVM initialization, extract the current state of the target JKS into an environment variable. This variable will hold the SHA-256 fingerprints of all existing entries for subsequent validation.

JKS_FILE="truststore.jks"
PASS_FILE="pass.txt"

export JKS_DUMP=$(LC_ALL=C keytool -list -keystore "$JKS_FILE" -storepass "$(cat "$PASS_FILE")")

This command uses keytool -list without the -v flag. It only extracts aliases, dates, and fingerprints. It is sufficient for cryptographic comparison but lacks full Subject/Issuer metadata.

Validating PEM Certificates Against JKS Dump

This script parses the source PEM file, computes the SHA-256 fingerprint for each block using openssl, and compares it strictly against the $JKS_DUMP variable. This prevents duplicate imports and verifies the presence of required CA nodes.

PEM_FILE="chain.pem"

awk -F'\n' '
  BEGIN { cmd = "openssl x509 -noout -subject -fingerprint -sha256" }
  /-----BEGIN CERTIFICATE-----/ { cert = "" }
  { cert = cert $0 "\n" }
  /-----END CERTIFICATE-----/ {
    print cert | cmd
    close(cmd)
  }
' "$PEM_FILE" | while read -r line; do
    if [[ "$line" == subject=* ]] || [[ "$line" == subject* ]]; then
        SUB="${line#*=}"
    elif [[ "$line" == SHA256* ]]; then
        FP="${line#*=}"
        if echo "$JKS_DUMP" | grep -q "$FP"; then
            echo "[PRESENT] $SUB"
        else
            echo "[MISSING] $SUB"
        fi
    fi
done

This script relies on shell string parsing. Variations in OpenSSL distributions might alter the subject= output format. The fingerprint evaluation remains mathematically accurate, but the displayed subject strings might break.

Importing Certificate Chain into JKS

This generic script splits a multi-certificate PEM file into individual temporary files and imports them sequentially into the target JKS. It operates independently of the validation step. To avoid alias collision, it generates a unique alias name based on the certificate’s SHA-256 hash prefix.

JKS_FILE="truststore.jks"
PASS_FILE="pass.txt"
PEM_FILE="chain.pem"

# 1. Split the multi-block PEM file into individual temporary files
awk '/-----BEGIN CERTIFICATE-----/{c++} {print > ("temp_cert_" c ".pem")}' "$PEM_FILE"

# 2. Iterate and import each file into the JKS
for cert_file in temp_cert_*.pem; do
    # Extract an 8-character prefix from the SHA-256 hash to ensure uniqueness
    fp_prefix=$(openssl x509 -in "$cert_file" -noout -fingerprint -sha256 | awk -F'=' '{print $2}' | tr -d ':' | cut -c 1-8 | tr 'A-Z' 'a-z')
    alias_name="ca_node_${fp_prefix}"

    keytool -importcert -file "$cert_file" \
            -keystore "$JKS_FILE" \
            -alias "$alias_name" \
            -storepass "$(cat "$PASS_FILE")" \
            -trustcacerts -noprompt
done

# 3. Clean up temporary operational files
rm temp_cert_*.pem

Importing a leaf (server) certificate into a truststore introduces Certificate Pinning, which creates operational fragility. Ensure the source PEM only contains Root and Intermediate CA certificates unless pinning is explicitly required by the architecture.