A Java keystore holds the private keys and certificates that let a Java service prove who it is, so a copied keystore file and its password are a working machine identity in someone else's hands. JKS (Java KeyStore) was the default format for two decades, PKCS#12 replaced it as the default in JDK 9, and most estates still run a mix of both, plus truststores, the JDK cacerts file and Android keystores that work in a completely different way. This guide explains the formats and how they protect keys, how to choose between them, how to handle keystore passwords, how to migrate and rotate without outages, and what to monitor. It is written for Java developers, platform engineers and the security teams who inherit keystores nobody remembers creating.
Key takeaways
- A keystore is a credential, not a config file. Whoever holds the file and its password can impersonate the service, decrypt traffic captured with non-forward-secret ciphers or sign as it, so treat it like any other non-human identity secret.
- PKCS#12 is the default and the right choice for new stores. Since JDK 9 (JEP 229)
keystore.type=pkcs12, and current JDKs protect PKCS#12 keys with PBES2 and AES-256 by default; JKS and JCEKS are proprietary formats with no reason to be created today. - Converting the file does not upgrade old entries. Loading and saving an existing PKCS#12 store keeps its old algorithms; only
keytool -importkeystoreinto a new store applies the current defaults. - The password is the weak point.
changeit, passwords on the command line and passwords committed next to the file in Git are the common failures. Hold the password in a secrets manager and inject it at runtime. - Truststores decide who you trust. A bloated or stale truststore, or a custom one that silently replaces
cacerts, is as risky as a leaked key. Inventory both, with expiry dates.
Keystore formats and what they contain
The Java KeyStore API is a single interface over several storage formats. The Java Security Standard Algorithm Names specification lists five built-in keystore types, and the keytool documentation notes that type names are case-insensitive. A keystore can hold three kinds of entry: private key entries (a private key with its certificate chain, used to prove identity), trusted certificate entries (a public certificate you trust, used to verify others) and, in formats that support them, secret key entries (symmetric keys).
| Type | What it is | Typical file | Use today |
|---|---|---|---|
pkcs12 | Standard format defined by PKCS #12 (RFC 7292), the JDK default since JDK 9; stores private keys, certificates and secret keys | .p12, .pfx | Default for all new keystores and truststores |
jks | Proprietary format from the SUN provider; private keys and trusted certificates only | .jks, .keystore | Legacy only; migrate |
jceks | Proprietary format from the SunJCE provider; added secret key entries | .jceks | Legacy only; migrate to PKCS#12 |
pkcs11 | Keystore backed by a PKCS #11 token such as an HSM or smart card; keys never sit in a file | Provider configuration file | High-value keys: CA, code signing, token signing |
dks | Domain keystore: several keystores presented as one logical store, configured through DomainLoadStoreParameter | Configuration file | Niche; rarely needed |
AndroidKeyStore | Android provider (API level 18 and later) that keeps key material out of the app process, optionally in a TEE or StrongBox secure element | No file | Runtime keys in Android apps |
Two terms are often confused. A keystore holds your own private keys and is what a server or client presents during TLS or signing. A truststore holds the certificates you accept from others. In Java both are the same file type: the difference is only how the application uses it. The JDK ships a default truststore, cacerts, in $JAVA_HOME/lib/security; its initial password is the widely published changeit.
How PKCS#12 protects what it holds
RFC 7292 (PKCS #12 v1.1, an Informational RFC since updated by RFC 9579 and RFC 9879) defines a container of "safe bags" (key bags, shrouded PKCS#8 key bags, certificate bags, CRL bags and secret bags) with two independent protections: privacy (encryption of the contents) and integrity (a password-based MAC or a signature over the whole file). The RFC deprecates the old password-privacy method for new use in favour of PBES2 with PBKDF2 from PKCS #5 v2.1.
In the JDK, the algorithms are controlled by security properties in $JAVA_HOME/conf/security/java.security. In JDK 21 the defaults are:
| Property | JDK 21 default | Legacy value (with keystore.pkcs12.legacy) |
|---|---|---|
keystore.pkcs12.keyProtectionAlgorithm | PBEWithHmacSHA256AndAES_256 | PBEWithSHA1AndDESede |
keystore.pkcs12.certProtectionAlgorithm | PBEWithHmacSHA256AndAES_256 | PBEWithSHA1AndRC2_40 |
keystore.pkcs12.macAlgorithm | HmacPBESHA256 | HmacPBESHA1 |
keystore.pkcs12.keyPbeIterationCount / certPbeIterationCount | 10000 | 50000 |
keystore.pkcs12.macIterationCount | 10000 | 100000 |
OpenSSL 3 has the same split: openssl pkcs12 -export uses AES-256-CBC with PBKDF2 and a SHA-256 MAC by default, and the -legacy flag loads the legacy provider for RC2 and 3DES files. Interoperability problems usually come from an old library on the other side that cannot read PBES2. The fix is to upgrade that library, not to downgrade every keystore; if a legacy file is unavoidable, scope it to that one integration and record it as an exception.
Whatever the algorithm, password-based encryption is only as strong as the password. An attacker who copies a keystore can test passwords offline without limit, so a short or reused password undoes AES-256. The iteration count slows guessing; it does not stop it.
Choosing a format and a storage model
The format question is settled for new work. The more important decision is where the private key lives and how the application gets it.
| Model | How it works | Choose when | Trade-offs |
|---|---|---|---|
| PKCS#12 file on disk, password from a secrets manager | File mounted read-only; password injected at start-up | Most application servers and services | File can be copied; rotation means replacing the file and restarting or reloading |
| PEM key and certificate files | Unencrypted or encrypted PEM read by the framework (for example Spring Boot PEM SSL bundles) | Certificates issued by ACME, cert-manager or a cloud CA as PEM | Unencrypted PEM relies entirely on file permissions; simpler automated rotation |
| PKCS#11 (HSM or cloud HSM) | Key generated and used inside hardware; Java calls it through SunPKCS11 | CA keys, code signing, token signing, regulated keys | Cost, latency and availability planning; keys cannot be exported, which is the point |
| Keystore delivered by a secrets platform | Vault, cloud secrets manager or Kubernetes Secret writes the file at runtime | Short-lived certificates and automated issuance | Moves trust to the platform's identity and access policies |
| AndroidKeyStore | Keys created on the device and never exported; use can require user authentication | Mobile app runtime keys | Not a place to put a server key; behaviour depends on device hardware |
- Small teams: PKCS#12 files with unique generated passwords held in a cloud secrets manager, and a short list of where each file lives, cover most of the risk.
- Large estates: move to automated issuance with short certificate lifetimes, so that keystores become disposable output of a pipeline rather than hand-made artefacts. See the Machine Identity, PKI and Certificate Lifecycle Guide.
- Signing keys that others rely on (SSO token signing, code signing, SAML signing): put them in an HSM. See the HSM Deployment and Operations Guide.
Keystore passwords and how to supply them
Java keystores have a store password (integrity and, in PKCS#12, encryption of the container) and a key password per private key entry. The keytool documentation states that both must be at least six characters, that pressing Return at the key password prompt sets the key password equal to the store password, and that when PKCS#12 files will be read by third-party tools you should always make -destkeypass the same as -deststorepass. In practice, one strong store password per keystore is the norm.
- Generate, do not choose: use a random password of at least 20 characters from a secrets manager, unique per keystore and per environment. Never reuse the development password in production.
- Keep it off the command line:
-Djavax.net.ssl.keyStorePassword=...andkeytool -storepass ...appear in process listings, shell history and CI logs. Prefer a file descriptor, an environment variable injected by the platform, or a framework property resolved from a secrets backend. - Keep it away from the file: a password in
application.propertiesin the same repository or image as the.p12file gives an attacker both halves. In Spring Boot,spring.ssl.bundle.jks.<name>.keystore.passwordshould resolve from an external source, not a literal. - Change the
cacertspassword or stop writing to it: anyone who can write to the JDK directory and knowschangeitcan add a trusted root. Keep application trust in a separate, application-owned truststore and keep the JDK directory read-only. - Interactive prompts for operators:
keytoolprompts when-storepassis omitted, and-protectedsupports a protected authentication path such as a PIN pad for hardware tokens.
See the Secrets Management Guide for vault patterns and the Secrets Zero Problem Guide for how a service gets its first secret.
Truststores, cacerts and trust decisions
The JSSE TrustManagerFactory uses the file named in javax.net.ssl.trustStore when that property is set; otherwise the JDK falls back to jssecacerts and then cacerts in $JAVA_HOME/lib/security. Setting a custom truststore replaces the default entirely, which is why "TLS broke after we added our internal CA" incidents are common: the custom file held only the internal root.
- Separate trust by purpose: public web trust (the JDK
cacerts, updated with the JDK), internal service trust (your private CA roots only) and partner trust (specific partner CAs for mTLS). Mixing them widens who can authenticate to what. See the mTLS Guide. - Trust roots, not leaves: pinning individual server certificates in a truststore breaks on every renewal, and short certificate lifetimes make that worse. Trust the issuing root or intermediate and validate the hostname.
- Remove what you no longer trust: a decommissioned partner CA left in a truststore is a standing back door. When a CA is distrusted, follow the CA Compromise and Mass Certificate Replacement Playbook.
- Keep the JDK current: the bundled
cacertsand thejdk.certpath.disabledAlgorithmsandjdk.tls.disabledAlgorithmslists change with JDK updates. In JDK 21,jdk.tls.disabledAlgorithmsalready disables SSLv3, TLS 1.0 and 1.1, RC4, DES and 3DES, andkeytoolwarns about anything injdk.security.legacyAlgorithms, such as SHA-1 and RSA keys under 2048 bits.
Creating, migrating and rotating keystores
The keytool defaults in JDK 21 are reasonable: -keyalg has no default and must be given, RSA keys default to 3072 bits and EC keys to 384 bits, the signature algorithm follows the key (for example SHA384withRSA), and -validity defaults to 90 days. The defaults are updated with each release, so state algorithm, size and validity explicitly in scripts.
Migrating JKS and JCEKS to PKCS#12
The keystore.type.compat=true setting (the JDK 21 default) lets the JKS and PKCS12 implementations each load files in either format, which hides the problem: a file named .jks may already be PKCS#12, and a JKS file keeps working until someone turns compatibility off. Check the real type with keytool -list -v, which prints the keystore type, every alias, entry type, certificate owner, issuer, serial number, validity dates and extensions.
- Inventory: list every keystore and truststore, its real type, owner, aliases, expiry dates and where its password is held.
- Convert into a new file:
keytool -importkeystorewith-srcstoretype JKSand-deststoretype PKCS12into a new destination, never over the original. As the JDK notes injava.security, only a fresh store picks up the current PBES2 and AES-256 defaults; re-saving an old store keeps its old algorithms. - Verify: run
keytool -list -von the result and compare aliases and certificate fingerprints, and check the algorithms withopenssl pkcs12 -info -noout. - Test in a non-production environment: start the service with the new file and test TLS handshakes from every known client, including old ones.
- Cut over with a rollback: deploy the new file and new password together, keep the old file in a protected location for one release, then destroy it.
- Rename honestly: rename
.jksfiles to.p12once converted, so future readers are not misled.
Rotation without outages
- Overlap: add the new certificate alias, deploy, switch the application to it, then remove the old alias after every client has moved.
- Reload rather than restart: Spring Boot can watch PEM bundles on
file:locations withspring.ssl.bundle.pem.<name>.reload-on-update=true, and Tomcat and Netty then rotate certificates without a restart. Note that the documented hot reload applies to PEM bundles, not JKS or PKCS#12 bundles. - Rotate the password too: changing the certificate but keeping the same store password for years means one past leak still opens every future file.
- Plan for shorter lifetimes: public TLS certificate lifetimes are falling, so manual keystore renewal will not scale. See the Certificate Expiry Outages and 47-Day TLS Certificates Guide.
Where identity fits
Every private key entry in a keystore is a non-human identity: a workload proving who it is to another system. The usual NHI questions apply. Who owns it? What can it reach? When does it expire? Who else has a copy? Keystores also travel badly: they are copied into container images, attached to tickets, shared between environments and baked into vendor appliances. See the Private Keys in Repositories Guide and the Secrets in Container Images and Build Artefacts Guide.
The highest-impact keystores are the ones that sign tokens or assertions for other systems, such as SSO, SAML and OIDC signing keys. A copy of one of those lets an attacker forge identities for every relying party. The Microsoft Storm-0558 key breach showed what a stolen signing key can do at scale, even though that key was not held in a Java keystore.
Detection and measurement
- Find keystores where they should not be: scan repositories, images and artefact stores for
.jks,.p12,.pfx,.jceksand.keystorefiles, and for properties such askeyStorePasswordorkeystore.passwordwith literal values. See the Git Secret Scanning Guide. - Watch the files on hosts: file integrity monitoring on keystore paths and on
$JAVA_HOME/lib/securitycatches unexpected reads by unusual processes and writes tocacerts. - Track expiry: scheduled
keytool -list -vor a certificate inventory tool that reads keystores, alerting at 30 and 7 days. - Watch for use elsewhere: Certificate Transparency monitoring shows public certificates issued for your names, and TLS telemetry on internal traffic can show your client certificate presented from unexpected hosts.
| Metric | What it shows | Target |
|---|---|---|
| Keystores in inventory with a named owner | Coverage of the estate | 100% |
| Keystores still in JKS or JCEKS format | Migration progress | Falling to zero, with recorded exceptions |
| Keystores whose password is held in a secrets manager | Password hygiene | 100% in production |
| Keystore files found in Git, images or tickets | Sprawl | Zero, each finding rotated |
| Certificates expiring without a renewal ticket inside 30 days | Rotation discipline | Zero |
| Median certificate lifetime in keystores | Readiness for automation | Falling year on year |
Common failure patterns
- The keystore and its password ship together: a
.p12file and anapplication.propertiesfile with its password in the same Git repository or container layer. Rotate both and remove them from history. - Default and shared passwords:
changeiton truststores and keystores, or one password used across every environment, so one leak opens everything. - Legacy identity middleware with live keystores: the Oracle Cloud login server claim included Java keystore files and key files from SSO servers that Oracle later described as obsolete. Whatever the final account, decommissioning a server is not complete until its keys are revoked and reissued.
- Re-saving instead of re-creating: a team "upgrades" a PKCS#12 file by loading and saving it, which keeps the old 3DES and SHA-1 protection.
- A custom truststore that drops public trust, or keeps everything: either outbound TLS fails after a change, or old partner and test CAs stay trusted for years.
- Keys and certificates copied into other systems: every copy of a key in a backup, image, snapshot or third-party service is another place it can leak. In the Imperva breach a database snapshot exposed SSL certificates customers had uploaded, and Imperva reported that about 13,500 certificates were rotated afterwards.
Framework mapping
| Framework | Reference | How it applies |
|---|---|---|
| NIST SP 800-53 Rev. 5 | SC-12 Cryptographic Key Establishment and Management; SC-17 Public Key Infrastructure Certificates; IA-5 Authenticator Management; SC-28 Protection of Information at Rest | Key lifecycle, certificate issuance, password and key protection, encrypted storage |
| NIST CSF 2.0 | PR.AA-01; PR.DS-01 | Credentials for services are managed; data at rest (including key files) is protected |
| MITRE ATT&CK v20 | T1552.004 Private Keys; T1552.001 Credentials In Files; T1649 Steal or Forge Authentication Certificates | Theft of keystore files and passwords, and misuse of the certificates they hold |
| MITRE D3FEND | D3-CH Credential Hardening | Strong unique passwords, hardware-backed keys and rotation |
| ISO/IEC 27001:2022 Annex A | A.8.24 Use of cryptography | Rules for key management across the lifecycle |
Practitioner checklist
- Build an inventory of every keystore and truststore with its real type, owner, aliases, expiry dates and password location.
- Create all new keystores as PKCS#12 with explicit algorithm, key size and validity in scripts.
- Migrate JKS and JCEKS files with
keytool -importkeystoreinto new files, verify with-list -vandopenssl pkcs12 -info, then destroy the originals. - Give each keystore a unique generated password held in a secrets manager, never on the command line or beside the file.
- Keep the JDK directory read-only, stop modifying
cacerts, and use application-owned truststores separated by purpose. - Move signing keys that other systems trust into an HSM through PKCS#11.
- Scan repositories, images and artefact stores for keystore files and literal keystore passwords, and rotate anything found.
- Alert on certificate expiry at 30 and 7 days and move towards automated issuance and reload.
- When a server holding keystores is retired or compromised, revoke and reissue its certificates and keys, not just the server.
Standards and references
- Oracle: keytool command reference (Java SE 21)
- OpenJDK: JEP 229 Create PKCS12 Keystores by Default
- Oracle: Java Security Standard Algorithm Names (KeyStore types)
- IETF: RFC 7292 PKCS #12 Personal Information Exchange Syntax v1.1
- Oracle: JSSE Reference Guide (Java SE 21)
- OpenSSL: openssl-pkcs12 manual
- Spring: Spring Boot SSL bundles
- Android Developers: Android Keystore system
Related NHI Mgmt Group resources: Java and Spring Security Guide · Machine Identity, PKI and Certificate Lifecycle Guide · mTLS Guide · HSM Deployment and Operations Guide · Secrets Management Guide