Java Keytool is the JDK’s built-in certificate and key management utility. It manages keystores: container files that hold private keys, certificates, and trusted CA certificates, each identified by an alias. Understanding a small set of keytool commands covers the vast majority of day-to-day Java TLS administration tasks.
One thing to know before starting: since JDK 9, PKCS12 is the default keystore format and JKS (Java KeyStore) is deprecated. JKS is a proprietary Java format that cannot be used directly with OpenSSL or other TLS toolkits. If you are creating new keystores, create them as PKCS12. If you are maintaining old JKS keystores, plan to migrate. The commands in this reference work for both formats; differences are noted where they exist.
This reference is organized by task. Find the task you need, copy the command, and adjust the file names and aliases for your environment.
Keystore Fundamentals: JKS vs PKCS12, Keystores vs Truststores
| Concept | What It Is | Default File | Default Password |
| Keystore (PKCS12) | Holds your server’s private key and certificate chain. Used when your Java application presents a certificate to other parties. | No default; you create it | You set it at creation |
| Truststore (cacerts) | Holds trusted CA certificates. Java uses this to validate certificates presented by remote servers (outbound TLS). | $JAVA_HOME/lib/security/cacerts | changeit (change this in production) |
| JKS | Deprecated proprietary Java keystore format. Works but cannot be used with non-Java tools. | keystore.jks | You set it at creation |
| PKCS12 (.p12 or .pfx) | Industry-standard keystore format. Interoperable with OpenSSL, Windows, macOS, and other tools. Default since JDK 9. | keystore.p12 | You set it at creation |
The default trust store password is literally changeit. This is widely known and has been the default since Java’s earliest versions. Production Java deployments should change this, though in practice many do not. Changing it requires updating any tooling or configuration that references the cacerts file with the default password.
Inspecting Keystores and Certificates
Start here. Before making any changes to a keystore, list its contents to understand what is in it.
List all entries in a keystore
| # Basic listing (shows aliases and entry types only):
$ keytool -list -keystore keystore.p12 -storepass yourpassword
# Verbose listing (shows full certificate details for each entry): $ keytool -list -v -keystore keystore.p12 -storepass yourpassword
# For JKS format, add -storetype JKS: $ keytool -list -v -keystore keystore.jks -storetype JKS -storepass yourpassword
# List a specific alias only: $ keytool -list -v -keystore keystore.p12 -storepass yourpassword -alias myserver
# Understanding the output: # PrivateKeyEntry = private key + certificate chain (server cert or client cert) # trustedCertEntry = trusted CA certificate (for trust store use) # If you expect a PrivateKeyEntry but see trustedCertEntry, you imported # a certificate without its private key. |
Print certificate details from a file (without importing)
| # Print details of a PEM certificate file:
$ keytool -printcert -file certificate.pem
# Print details of a DER certificate file: $ keytool -printcert -file certificate.der
# Check a live server’s certificate without any local file: $ keytool -printcert -sslserver yourdomain.com:443 # This connects to the server and prints the certificate it presents. # Useful for verifying what is actually being served vs what is in the keystore.
# Print verbose output from the server certificate: $ keytool -printcert -v -sslserver yourdomain.com:443 |
The -sslserver flag is one of keytool’s most useful but underused features. It shows you exactly what certificate a live server is presenting, including the full SAN list, expiry date, and issuer chain. This is faster than OpenSSL s_client for a quick certificate check and requires no OpenSSL installation.
Creating a New Keystore and Key Pair
Create a new PKCS12 keystore with a key pair (modern approach)
| # Generate a new RSA key pair and self-signed certificate in a PKCS12 keystore:
$ keytool -genkeypair \ -alias myserver \ -keyalg RSA \ -keysize 2048 \ -storetype PKCS12 \ -keystore keystore.p12 \ -storepass yourpassword \ -validity 365 \ -dname ‘CN=yourdomain.com, OU=IT, O=YourOrg, L=City, ST=State, C=US’ \ -ext SAN=dns:yourdomain.com,dns:www.yourdomain.com,ip:192.168.1.1
# Key algorithm options: RSA (most compatible), EC (elliptic curve, smaller keys) # For EC: -keyalg EC -groupname secp256r1 # The -ext SAN flag adds Subject Alternative Names (required by modern browsers)
# Verify the keystore was created correctly: $ keytool -list -v -keystore keystore.p12 -storepass yourpassword |
Generate a CSR from an existing keystore entry
After creating a key pair, generate a Certificate Signing Request to send to a CA. The CA returns a signed certificate that you then import back into the keystore.
| # Generate CSR from the private key in the keystore:
$ keytool -certreq \ -alias myserver \ -keystore keystore.p12 \ -storepass yourpassword \ -file myserver.csr \ -ext SAN=dns:yourdomain.com,dns:www.yourdomain.com
# Send myserver.csr to your CA. The CA returns a signed certificate.
# Verify the CSR contents: $ keytool -printcertreq -file myserver.csr |
Importing Certificates and Private Keys
Import a CA-signed certificate (reply to CSR) into keystore
After the CA issues the certificate in response to your CSR, import it to associate it with the private key already in the keystore.
| # First import the root CA certificate (required before importing the signed cert):
$ keytool -importcert \ -alias rootca \ -file root-ca.crt \ -keystore keystore.p12 \ -storepass yourpassword \ -trustcacerts
# Then import the intermediate CA certificate if there is one: $ keytool -importcert \ -alias intermediateca \ -file intermediate.crt \ -keystore keystore.p12 \ -storepass yourpassword \ -trustcacerts
# Finally import the signed server certificate (matches the alias from -genkeypair): $ keytool -importcert \ -alias myserver \ -file myserver-signed.crt \ -keystore keystore.p12 \ -storepass yourpassword
# The alias must match the alias used during -genkeypair. # If it does not match, keytool will add it as a trustedCertEntry # instead of associating it with the existing private key. |
Keytool cannot import a private key directly from a PEM file. If you have a private key and certificate from an external source (generated with OpenSSL, downloaded from a CA, exported from another system), you must first create a PKCS12 file using OpenSSL, then import the PKCS12 into the keystore. The direct private key import workflow is covered in the next section.
Import a private key and certificate from an external source (via PKCS12)
This is the workflow for importing a key and certificate pair that was not generated by keytool: for example, a certificate and key pair from Let’s Encrypt, OpenSSL, or another system.
| # Step 1: Create a PKCS12 file from the PEM key and certificate using OpenSSL:
$ openssl pkcs12 -export \ -inkey private.key \ -in certificate.pem \ -certfile ca-chain.pem \ -out bundle.p12 \ -name myserver \ -passout pass:p12password
# Step 2: Import the PKCS12 into an existing or new Java keystore: $ keytool -importkeystore \ -srckeystore bundle.p12 \ -srcstoretype PKCS12 \ -srcstorepass p12password \ -destkeystore keystore.p12 \ -deststoretype PKCS12 \ -deststorepass yourpassword \ -alias myserver
# The -alias flag in -importkeystore filters to import only that alias. # Omit -alias to import all entries from the source keystore. |
Add a trusted CA certificate to the JVM trust store
The most common reason for PKIX path building failed errors in Java is that the server’s CA certificate is not in the JVM’s cacerts trust store. Add it:
| # Add a CA certificate to the default JVM trust store:
$ keytool -importcert \ -alias corporateca \ -file corporate-ca.crt \ -keystore $JAVA_HOME/lib/security/cacerts \ -storepass changeit \ -trustcacerts
# You will be prompted: Trust this certificate? [no]: # Type yes and press Enter.
# For JDK 8 and earlier, the path is: # $JAVA_HOME/jre/lib/security/cacerts
# To add to a custom trust store instead of the system cacerts: $ keytool -importcert \ -alias corporateca \ -file corporate-ca.crt \ -keystore my-truststore.jks \ -storetype JKS \ -storepass trustorepassword \ -trustcacerts
# Then configure your JVM to use the custom trust store: # -Djavax.net.ssl.trustStore=/path/to/my-truststore.jks # -Djavax.net.ssl.trustStorePassword=trustorepassword |
Exporting Certificates and Converting Formats
Export a certificate from a keystore to a file
| # Export certificate to DER format (binary):
$ keytool -exportcert \ -alias myserver \ -keystore keystore.p12 \ -storepass yourpassword \ -file myserver.der
# Export certificate to PEM format (Base64): $ keytool -exportcert \ -alias myserver \ -keystore keystore.p12 \ -storepass yourpassword \ -rfc \ -file myserver.pem # The -rfc flag outputs PEM format instead of DER.
# Verify the exported certificate: $ keytool -printcert -file myserver.pem |
Convert JKS keystore to PKCS12 (migration path)
| # Convert JKS to PKCS12 (recommended migration for all JKS keystores):
$ keytool -importkeystore \ -srckeystore old-keystore.jks \ -srcstoretype JKS \ -srcstorepass jkspassword \ -destkeystore new-keystore.p12 \ -deststoretype PKCS12 \ -deststorepass p12password
# If the JKS has per-key passwords different from the store password: $ keytool -importkeystore \ -srckeystore old-keystore.jks \ -srcstoretype JKS \ -srcstorepass jkspassword \ -srckeypass keypassword \ -destkeystore new-keystore.p12 \ -deststoretype PKCS12 \ -deststorepass p12password
# Verify all entries migrated correctly: $ keytool -list -v -keystore new-keystore.p12 -storepass p12password |
In PKCS12, the key password and store password must be the same. JKS allows them to differ. If your JKS has different key and store passwords, you must specify -srckeypass during migration. After migration to PKCS12, only the store password matters for all operations.
Maintenance: Delete, Rename, and Password Operations
Delete a keystore entry
| # Delete an entry by alias:
$ keytool -delete \ -alias oldentry \ -keystore keystore.p12 \ -storepass yourpassword
# Confirm deletion by listing the keystore afterwards: $ keytool -list -keystore keystore.p12 -storepass yourpassword |
Rename an alias
| # Rename an existing alias to a new name:
$ keytool -changealias \ -alias oldname \ -destalias newname \ -keystore keystore.p12 \ -storepass yourpassword
# Aliases are case-sensitive in keytool operations. # If you receive ‘Alias does not exist’, check the exact alias name # with keytool -list before attempting the rename. |
Change keystore password
| # Change the keystore password:
$ keytool -storepasswd \ -keystore keystore.p12 \ -storepass currentpassword \ -new newpassword
# For the JVM cacerts trust store (changing the default ‘changeit’ password): $ keytool -storepasswd \ -keystore $JAVA_HOME/lib/security/cacerts \ -storepass changeit \ -new yoursecurepassword # Update any application configuration referencing this trust store # with the new password after changing it. |
Common Errors and What They Mean
| Error Message | Cause | Fix |
| PKIX path building failed: unable to find valid certification path | The server’s CA certificate is not in the JVM trust store | Add the CA certificate to cacerts or a custom trust store using -importcert -trustcacerts |
| Alias does not exist | The alias name is wrong, or case does not match | Run keytool -list first to see exact alias names; aliases are case-sensitive |
| java.io.IOException: Invalid keystore format | Trying to open a PKCS12 file as JKS or vice versa | Add -storetype PKCS12 or -storetype JKS to match the actual file format |
| Keystore was tampered with, or password was incorrect | Wrong password, or the file is corrupted | Verify the password; try with -storetype explicitly specified |
| Certificate reply does not contain public key for the certificate | Importing a certificate that does not match the private key in the keystore | Ensure the certificate was signed from the CSR generated by this keystore with this alias |
| Cannot store non-PrivateKeys | Attempting to import a self-signed cert as a private key entry | Use -trustcacerts flag when importing CA or trusted certificates; omit it for private key entries |
Quick Reference: Command Flags
| Flag | Purpose | Example |
| -keystore | Path to the keystore file | -keystore /etc/ssl/keystore.p12 |
| -storepass | Keystore password | -storepass yourpassword |
| -alias | Alias name for the entry | -alias myserver |
| -storetype | Keystore format: PKCS12, JKS, BKS | -storetype PKCS12 |
| -keyalg | Key algorithm: RSA, EC, DSA | -keyalg RSA |
| -keysize | Key length in bits for RSA | -keysize 2048 |
| -groupname | Named curve for EC keys | -groupname secp256r1 |
| -validity | Certificate validity in days | -validity 365 |
| -dname | Distinguished name for the certificate subject | -dname ‘CN=example.com,O=Org,C=US’ |
| -ext | Certificate extensions including SANs | -ext SAN=dns:example.com,ip:10.0.0.1 |
| -v | Verbose output | -v (no value) |
| -rfc | Output in PEM (RFC) format instead of DER | -rfc (no value) |
| -trustcacerts | Use when importing trusted CA certificates | -trustcacerts (no value) |
| -file | Input or output file | -file mycert.pem |
| -noprompt | Skip confirmation prompts (for scripting) | -noprompt (no value) |
Frequently Asked Questions
What is the difference between a keystore and a truststore?
A keystore holds private keys and the certificates associated with them. A Java application uses its keystore when it needs to present a certificate to another party, such as when acting as an HTTPS server or using mutual TLS as a client. A truststore holds trusted CA certificates. A Java application uses its truststore when it needs to validate a certificate presented by a remote server, checking whether the certificate chains to a trusted CA. In practice, the same keystore file can serve as both, with PrivateKeyEntry entries for keys and trustedCertEntry entries for trusted CAs. The JVM’s default truststore is the cacerts file in $JAVA_HOME/lib/security/.
Should I use JKS or PKCS12?
PKCS12. JKS is a proprietary Java format deprecated since JDK 9. PKCS12 is an industry standard that interoperates with OpenSSL, Windows Certificate Store, macOS Keychain, and all other TLS toolkits. If you need to convert a certificate from a Java keystore to use with Nginx or Apache, PKCS12 can be used directly with those tools. JKS cannot. Migrate existing JKS keystores to PKCS12 using the keytool -importkeystore command with -deststoretype PKCS12.
What does ‘PKIX path building failed’ mean and how do I fix it?
This is the most common Java TLS error. It means the JVM tried to validate the certificate presented by a server and could not trace it to any trusted CA in its trust store. The CA that signed the server’s certificate is not in the cacerts file. Fix it by importing the missing CA certificate into cacerts (keytool -importcert -alias caname -file ca.crt -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -trustcacerts) or into a custom trust store that you pass to the JVM with -Djavax.net.ssl.trustStore.
How do I import a private key into a Java keystore?
Keytool cannot import a private key directly from a PEM file. The workflow is: use OpenSSL to create a PKCS12 file containing the private key and certificate (openssl pkcs12 -export -inkey private.key -in cert.pem -out bundle.p12), then import the PKCS12 into the Java keystore using keytool -importkeystore with -srcstoretype PKCS12. This two-step process is required for any private key that was not originally generated by keytool.
How do I check when a certificate in a keystore expires?
Run keytool -list -v -keystore keystore.p12 -storepass yourpassword and look for the Valid from and until lines in the output for each entry. For a specific alias: keytool -list -v -keystore keystore.p12 -storepass yourpassword -alias myserver. The valid until date shows the certificate expiry. To check a live server’s certificate expiry without accessing the keystore directly, use keytool -printcert -sslserver yourdomain.com:443 and look for the valid until field.
My Tomcat application is showing SSL errors after I updated the certificate. What should I check?
First verify the keystore contains the new certificate: keytool -list -v -keystore /path/to/keystore.p12 -storepass yourpassword. Confirm the alias in the keystore matches the alias configured in Tomcat’s server.xml (the keystoreAlias attribute in the Connector element). Confirm the keystore path and password in server.xml match the current file and password. Check that the entry type is PrivateKeyEntry, not trustedCertEntry, which would mean the certificate was imported without its private key. After verifying all of this, restart Tomcat and check the Tomcat log for SSL initialization errors on startup.
