Renew or Replace an Expired Certificate in an Existing Java Keystore

Renew an expired TLS certificate in an existing Java PKCS12 keystore by checking expiry with keytool -list -v, exporting a CSR from the same alias, and importing the CA-signed reply without deleting the private key.

Published

Updated

Read time 8 min read

Reviewed byDeepak Prasad

Renew expired Java keystore certificate banner with CSR and import workflow

Your Tomcat, Spring Boot, or Kafka broker certificate is nearing expiry — or already expired — and the private key must stay in the same PKCS12 file. Renewal is not a new keytool -genkeypair unless policy requires it: you export a fresh CSR from the existing alias, get a new signed certificate from your CA, and import the reply on that same alias.

This guide runs the full renewal loop on Ubuntu with a one-day lab certificate so you can see expiry change in keytool -list -v without waiting a year. For the initial CSR workflow, see Create a CSR with keytool and import a CA-signed certificate.

Tested on: Ubuntu 26.04 LTS; OpenJDK 25.0.3; kernel 7.0.0-27-generic.


Prerequisites


Reuse the key or generate a new one?

Situation Action
Standard TLS renewal, same hostname keytool -certreq on existing alias → import reply
CA mandates fresh key / new CSR key New -genkeypair or new alias, then cut over in config
Compromised private key New key pair; do not renew on the old alias
Expired trusted CA cert only (trustedCertEntry) Delete + -importcert on CA alias — not a CSR reply

This page focuses on PrivateKeyEntry renewal — the path WSO2, Coverity, and most Tomcat admins use.


Before you start

Back up the keystore and record the alias your application uses:

bash
cp server.p12 "server.p12.bak.$(date +%F)"
keytool -list -keystore server.p12 -storetype PKCS12 -storepass PASS

Note Tomcat keyAlias (older Connector style) or certificateKeyAlias (<Certificate> config), or Spring Boot server.ssl.key-alias. Import must target that exact alias.

If the import succeeds but the service fails after restart, restore the backup file and restart again while you inspect alias, chain, or key-password issues.

NOTE
For production systems, avoid leaving real keystore passwords in shell history. Use an interactive prompt, protected environment variable, secret manager, or temporary root-only script when possible.

Check certificate expiry

Read the server alias before requesting a new certificate:

bash
keytool -list -v -keystore server.p12 -storetype PKCS12 -storepass changeit -alias https \
  | grep -E "Alias name:|Entry type:|Valid from:|until:"

On a short-lived lab cert:

text
Alias name: https
Entry type: PrivateKeyEntry
Valid from: Fri Jul 03 14:16:16 IST 2026 until: Sat Jul 04 14:16:16 IST 2026

When until: is in the past or within your change window, proceed with renewal. An expired certificate does not prevent keytool -certreq from creating a new CSR from the existing private key — expiry affects TLS validation, not private-key reuse.

For auditing every alias, see List and inspect keystore entries.


Renewal workflow at a glance

Step Command Goal
1 Back up server.p12 Rollback if import fails
2 keytool -list -v Confirm alias and expiry
3 keytool -certreq CSR from same alias
4 Submit CSR to CA Receive signed .crt
5 Import CA chain if missing Root/intermediate as trustedCertEntry
6 keytool -importcert on server alias Install reply
7 keytool -list -v Confirm new until: date
8 Restart app Load new cert

Lab — renew a one-day certificate

The lab uses a local intermediate CA. Create CA files once under ~/renew-lab/ca/ (same OpenSSL steps as the CSR import guide), then work in ~/renew-lab/server/.

Step 1 — Bootstrap keystore (simulates your existing file)

bash
mkdir -p ~/renew-lab/server && cd ~/renew-lab/server

keytool -genkeypair -alias https -keyalg RSA -keysize 2048 -validity 1 \
  -dname "CN=app.lab.local" \
  -ext "SAN=DNS:app.lab.local,IP:127.0.0.1" \
  -keystore server.p12 -storetype PKCS12 -storepass changeit -noprompt
text
Generating 2048-bit RSA key pair and self-signed certificate (SHA384withRSA) with a validity of 1 days
	for: CN=app.lab.local

Step 2 — Export CSR from the same alias

Include the same SAN extensions your clients need:

bash
keytool -certreq -alias https -keystore server.p12 -storetype PKCS12 \
  -storepass changeit -file renew.csr \
  -ext "SAN=DNS:app.lab.local,IP:127.0.0.1"

Inspect the CSR:

bash
keytool -printcertreq -file renew.csr | grep -A4 "SubjectAlternativeName"
text
SubjectAlternativeName [
  DNSName: app.lab.local
  IPAddress: 127.0.0.1
]

Step 3 — Sign with your CA

For a public CA, upload renew.csr to the portal. In the lab, sign with the intermediate.

OpenSSL does not copy CSR extensions into the issued certificate by default. Pass -copy_extensions copy so SAN entries from the CSR survive signing. The same signing step on a live server certificate is covered in renew an SSL/TLS server certificate with OpenSSL.

bash
openssl x509 -req -in renew.csr \
  -CA ../ca/intermediate-ca.crt -CAkey ../ca/intermediate-ca.key \
  -CAcreateserial -out renew-signed.crt -days 365 -sha256 \
  -copy_extensions copy

Verify the signed certificate before import:

bash
openssl x509 -in renew-signed.crt -noout -subject -issuer -dates -ext subjectAltName
text
subject=CN=app.lab.local
issuer=C=US, O=GoLinuxCloud, CN=Lab Intermediate CA
notBefore=Jul  3 08:46:24 2026 GMT
notAfter=Jul  3 08:46:24 2027 GMT
X509v3 Subject Alternative Name:
    DNS:app.lab.local, IP Address:127.0.0.1

Without -copy_extensions copy, openssl x509 -noout -ext subjectAltName may report No extensions in certificate even when the CSR contained SAN — and Java clients can fail with No subject alternative names present after renewal.

Step 4 — Import CA chain (first renewal only)

If root and intermediate are not already in the keystore:

bash
keytool -importcert -alias labroot -file ../ca/root-ca.crt \
  -keystore server.p12 -storetype PKCS12 -storepass changeit -noprompt

keytool -importcert -alias labintermediate -file ../ca/intermediate-ca.crt \
  -keystore server.p12 -storetype PKCS12 -storepass changeit -noprompt
text
Certificate was added to keystore
Certificate was added to keystore

On later renewals with the same intermediate CA, skip this step — the chain entries already exist.

Step 5 — Import the signed reply on alias https

bash
keytool -importcert -alias https -file renew-signed.crt \
  -keystore server.p12 -storetype PKCS12 -storepass changeit -noprompt

Success:

text
Certificate reply was installed in keystore

If you see Certificate was added to keystore on a server alias, you imported into the wrong entry — the reply did not attach to your private key.

Step 6 — Verify new expiry and entry type

bash
keytool -list -v -keystore server.p12 -storetype PKCS12 -storepass changeit -alias https \
  | grep -E "Entry type:|Issuer:|Valid from:|until:"
text
Entry type: PrivateKeyEntry
Issuer: CN=Lab Intermediate CA, O=GoLinuxCloud, C=US
Valid from: Fri Jul 03 14:16:24 IST 2026 until: Sat Jul 03 14:16:24 IST 2027

The until: date moved forward. The entry remains PrivateKeyEntry — the private key was not regenerated.


Second renewal without re-importing CAs

When labintermediate is already a trustedCertEntry, submit a new CSR, sign it, and import only the leaf:

bash
keytool -certreq -alias https -keystore server.p12 -storetype PKCS12 \
  -storepass changeit -file renew2.csr \
  -ext "SAN=DNS:app.lab.local,IP:127.0.0.1"

openssl x509 -req -in renew2.csr \
  -CA ../ca/intermediate-ca.crt -CAkey ../ca/intermediate-ca.key \
  -CAcreateserial -out renew2-signed.crt -days 730 -sha256 \
  -copy_extensions copy

openssl x509 -in renew2-signed.crt -noout -dates -ext subjectAltName

keytool -importcert -alias https -file renew2-signed.crt \
  -keystore server.p12 -storetype PKCS12 -storepass changeit -noprompt
text
Certificate reply was installed in keystore
text
Entry type: PrivateKeyEntry
Certificate chain length: 3
Valid from: Fri Jul 03 14:16:39 IST 2026 until: Sun Jul 02 14:16:39 IST 2028

Re-import intermediate/root CAs when your CA rotates signing certificates — otherwise you may hit Failed to establish chain from reply.


Renew a trusted CA entry only

If you are renewing an expiring trustedCertEntry (not the server identity key), keytool -importcert cannot overwrite the alias. Delete the old CA entry, then import the new file:

bash
keytool -delete -alias labintermediate -keystore trust.p12 -storetype PKCS12 \
  -storepass changeit -noprompt

keytool -importcert -alias labintermediate -file new-intermediate.crt \
  -keystore trust.p12 -storetype PKCS12 -storepass changeit -noprompt

See Certificate not imported, alias already exists when renewal scripts hit alias collisions on trusted certs.


Apply the renewed keystore to Tomcat or Spring Boot

Point config at the same file path you edited (or swap in the backup-replaced file):

Platform Settings
Tomcat older/direct Connector style keystoreFile, keystorePass, keystoreType="PKCS12", keyAlias
Tomcat <Certificate> config certificateKeystoreFile, certificateKeystorePassword, certificateKeystoreType="PKCS12", certificateKeyAlias
Spring Boot server.ssl.key-store, server.ssl.key-store-password, server.ssl.key-store-type=PKCS12, server.ssl.key-alias

Restart or reload the service. Confirm the live certificate — notAfter should match the renewed until: date from keytool -list -v:

bash
openssl s_client -connect 127.0.0.1:8443 -servername app.lab.local -showcerts </dev/null 2>/dev/null \
  | openssl x509 -noout -dates -subject
text
notBefore=Jul  3 08:46:24 2026 GMT
notAfter=Jul  3 08:46:24 2027 GMT
subject=CN=app.lab.local

If the dates did not change, the JVM may still be reading an old file path or mount — see Invalid keystore format and verify the path inside the container.

After renewal, if the app starts but TLS handshakes fail with Cannot recover key, check key password alignment in UnrecoverableKeyException.


Troubleshooting renewal imports

Symptom Likely cause Fix
Public keys in reply and keystore don't match Reply imported on wrong alias Use the alias from -certreqpublic keys mismatch
Certificate was added to keystore on server alias Reply not matched to PrivateKeyEntry Wrong alias or wrong keystore file
App still serves old expiry Service not restarted or wrong path Restart; verify with openssl s_client

References


Summary

To renew an expiring PrivateKeyEntry, back up the PKCS12 file, confirm expiry with keytool -list -v, run keytool -certreq on the same alias, verify SAN on the signed .crt before import, and install the reply with keytool -importcert. Look for Certificate reply was installed in keystore. Re-import intermediate CAs only when the chain changes. Restart Tomcat or Spring Boot and verify the new notAfter date on the live HTTPS port.


Frequently Asked Questions

1. Do I need a new private key to renew an expiring certificate?

No for standard renewal. Export a new CSR with keytool -certreq from the same PrivateKeyEntry alias, have the CA sign it, and import the reply with keytool -importcert on that alias. The private key stays in the keystore. Generate a new key pair only when policy or the CA requires a fresh CSR key.

2. How do I check certificate expiry in a Java keystore?

Run keytool -list -v -keystore store.p12 -storetype PKCS12 -storepass PASS and read Valid from and until under the server alias. Add -alias NAME when the keystore holds multiple entries.

3. Should I delete the old certificate before importing the renewal?

No for a PrivateKeyEntry. keytool -importcert on the existing CSR alias replaces the certificate chain while keeping the private key. Deleting the alias removes the key pair. For trustedCertEntry CAs only, delete then re-import — see the alias already exists guide.

4. What success message confirms a renewal import worked?

Certificate reply was installed in keystore means the signed certificate matched the private key on that alias. Certificate was added to keystore on a server alias usually means you imported into the wrong entry type or alias.

5. Do I need to re-import intermediate CA certificates every renewal?

Not when the same intermediate CA still signs your cert and its trustedCertEntry alias is already in the keystore. Re-import root or intermediate CAs when the CA rotates intermediates or you see Failed to establish chain from reply.

6. When must I restart Tomcat or Spring Boot after renewal?

Restart or reload the service after updating the keystore file on disk. Tomcat does not always pick up a replaced PKCS12 until the connector reloads. Verify the running endpoint with openssl s_client after restart.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …