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
- OpenJDK with
keytool— Install keytool on Ubuntu — see keytool command. opensslfor CSR signing ands_clientchecks in the lab (OpenSSL).
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:
cp server.p12 "server.p12.bak.$(date +%F)"
keytool -list -keystore server.p12 -storetype PKCS12 -storepass PASSNote 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.
Check certificate expiry
Read the server alias before requesting a new certificate:
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:
Alias name: https
Entry type: PrivateKeyEntry
Valid from: Fri Jul 03 14:16:16 IST 2026 until: Sat Jul 04 14:16:16 IST 2026When 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)
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 -nopromptGenerating 2048-bit RSA key pair and self-signed certificate (SHA384withRSA) with a validity of 1 days
for: CN=app.lab.localStep 2 — Export CSR from the same alias
Include the same SAN extensions your clients need:
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:
keytool -printcertreq -file renew.csr | grep -A4 "SubjectAlternativeName"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.
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 copyVerify the signed certificate before import:
openssl x509 -in renew-signed.crt -noout -subject -issuer -dates -ext subjectAltNamesubject=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.1Without -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:
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 -nopromptCertificate was added to keystore
Certificate was added to keystoreOn later renewals with the same intermediate CA, skip this step — the chain entries already exist.
Step 5 — Import the signed reply on alias https
keytool -importcert -alias https -file renew-signed.crt \
-keystore server.p12 -storetype PKCS12 -storepass changeit -nopromptSuccess:
Certificate reply was installed in keystoreIf 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
keytool -list -v -keystore server.p12 -storetype PKCS12 -storepass changeit -alias https \
| grep -E "Entry type:|Issuer:|Valid from:|until:"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 2027The 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:
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 -nopromptCertificate reply was installed in keystoreEntry type: PrivateKeyEntry
Certificate chain length: 3
Valid from: Fri Jul 03 14:16:39 IST 2026 until: Sun Jul 02 14:16:39 IST 2028Re-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:
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 -nopromptSee 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:
openssl s_client -connect 127.0.0.1:8443 -servername app.lab.local -showcerts </dev/null 2>/dev/null \
| openssl x509 -noout -dates -subjectnotBefore=Jul 3 08:46:24 2026 GMT
notAfter=Jul 3 08:46:24 2027 GMT
subject=CN=app.lab.localIf 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 -certreq — public 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.

