Table of Contents
A practical guide to completing your TLS certificate chain
TL;DR
Spring Boot Admin showed my application as REGISTERED → OFFLINE because the server certificate I presented during the health‑check was missing the intermediate CA.
Re‑exporting the keystore (PKCS #12 / JKS) with the full chain—leaf + intermediate (+ root)—cleared the error and the instance now sits happily in the UP state.
The Symptom
Right after deployment my Spring Boot app registered with Spring Boot Admin (SBA):
vbnetCopyEditEvent : REGISTERED
Seconds later it flipped to OFFLINE:
luaCopyEditSTATUS_CHANGED (OFFLINE)
org.springframework.web.reactive.function.client.WebClientRequestException:
PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
Translation: SBA couldn’t trust the certificate my app delivered during the /actuator/health
poll.
Why SBA trusts the first call but not the second
Direction | TLS client | TLS server | Result |
---|---|---|---|
App → SBA (registration) | Spring Boot App | Spring Boot Admin | Passes – chain is fine in SBA’s keystore |
SBA → App (health‑check) | Spring Boot Admin | Spring Boot App | Fails – chain is incomplete in App’s keystore |
SBA’s JVM already trusted the root CA that signed my cert.
What it didn’t receive was the intermediate CA sitting between the leaf and the root.
Diagnosing the chain
bashCopyEdit# List the current PFX/JKS
keytool -list -v -keystore processing.pfx -storepass ***** \
| grep 'Certificate chain' -A4
# Length = 1 ← only the leaf certificate present
If you need to inspect a PEM bundle:
bashCopyEditgrep -c "BEGIN CERTIFICATE" processing.pem # got 3? split them:
awk '/BEGIN CERT/{i++}{print > "part" i ".pem"}' processing.pem
part1
should be the server cert; part2
, part3
the intermediates/root.
Re‑building a full‑chain PKCS #12
I had:
dlq-processing.1086.sb.crt
— leafdlq-processing.1086.sb.p7b
— intermediate + rootdlq-processing.1086.sb.key
— private key
bashCopyEdit# 1. Extract the CA chain from the PKCS#7
openssl pkcs7 -print_certs \
-in dlq-processing.1086.sb.p7b \
-out chain.pem # intermediate + root
# 2. Export a NEW PKCS#12 with the full chain
openssl pkcs12 -export \
-inkey dlq-processing.1086.sb.key \
-in dlq-processing.1086.sb.crt \
-certfile chain.pem \
-name dlq-processing \
-out dlq-processing-full.p12
(Optional) convert to JKS:
bashCopyEditkeytool -importkeystore \
-srckeystore dlq-processing-full.p12 -srcstoretype PKCS12 \
-destkeystore dlq-processing-full.jks -deststoretype JKS \
-alias dlq-processing
Now:
bashCopyEditkeytool -list -v -keystore dlq-processing-full.p12 -storepass ***** \
| grep 'Certificate chain' -A4
# 3 certificates -> PrivateKeyEntry ✅
Redeploy → SBA handshake succeeds → status UP.
PEM alternative (Boot 2.7+ / 3.x)
If you prefer avoiding keystores entirely:
propertiesCopyEditserver.ssl.certificate=classpath:fullchain.pem # leaf + intermediate + root
server.ssl.certificate-private-key=classpath:server.key
Spring Boot builds an in‑memory keystore on startup.
Key takeaways
- Leaf + Intermediate (+ Root)
Always deliver the full chain; the client often has only the root in its trust‑store. -certfile
is the secret sauce.
OpenSSL’spkcs12 -export
uses only the first cert unless you add-certfile
(or OpenSSL 3’s-CAfile -chain
).- Keystore password ≠ private key.
A PKCS #12/JKS always needs a password; the handshake needs the private key. - Trust‑store vs. key‑store.
Trust‑store = just CA certs.
Key‑store = private key + full chain.
The whole detour cost me a few head‑scratches, but the fix boiled down to include the intermediate CA. Hopefully this write‑up saves you that debugging loop!