Skip to content
ecdsa.com

Verify a certificate chain · Java

How to verify a certificate chain against your own trust store in Java

To verify a certificate chain against your own trust store in Java, wrap the leaf and intermediates in a CertPath and validate it with CertPathValidator.getInstance("PKIX") and PKIXParameters built from your TrustAnchor. Validation either returns or throws CertPathValidatorException with a reason. This recipe checks a leaf → intermediate → root chain from a test CA and three chains that must fail, with the JDK only.

Tested with OpenJDK 25.0.4.1 (Eclipse Temurin 25.0.4.1+1, CertPathValidator PKIX).

VerifyChain.java — PKIX path validation against one trust anchor
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Set;
public class VerifyChain {
static final CertificateFactory X509;
static {
try {
X509 = CertificateFactory.getInstance("X.509");
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
static X509Certificate load(String path) throws Exception {
try (InputStream in = Files.newInputStream(Path.of(path))) {
return (X509Certificate) X509.generateCertificate(in);
}
}
/** Validates leaf + intermediates (leaf first, root excluded) against one trusted root. */
static void verify(List<X509Certificate> chain, X509Certificate root) throws Exception {
CertPath path = X509.generateCertPath(chain);
PKIXParameters params = new PKIXParameters(Set.of(new TrustAnchor(root, null)));
params.setRevocationEnabled(false); // a test CA publishes no CRL or OCSP; keep it on in production
CertPathValidator.getInstance("PKIX").validate(path, params); // throws if anything is wrong
}
public static void main(String[] args) throws Exception {
X509Certificate root = load("root.pem"), intermediate = load("intermediate.pem");
verify(List.of(load("leaf.pem"), intermediate), root);
System.out.println("valid: api.example.com -> Issuing CA 1 -> Root CA");
String[][] cases = {
{"impostor root", "leaf.pem", "intermediate.pem", "impostor-root.pem"},
{"expired leaf", "expired.pem", "intermediate.pem", "root.pem"},
{"missing intermediate", "leaf.pem", null, "root.pem"},
};
for (String[] c : cases) {
List<X509Certificate> chain = c[2] == null ? List.of(load(c[1])) : List.of(load(c[1]), load(c[2]));
try {
verify(chain, load(c[3]));
} catch (CertPathValidatorException e) {
System.out.println(c[0] + ": " + e.getMessage() + " (reason " + e.getReason() + ", index " + e.getIndex() + ")");
}
}
}
}

How it works

  1. CertificateFactory.generateCertPath(list) takes the chain leaf first, without the root; the root goes into a TrustAnchor.
  2. PKIXParameters(Set.of(new TrustAnchor(root, null))) trusts only that root. setRevocationEnabled(false) is needed here because the test CA publishes no CRL or OCSP: with the default (revocation on), the same chain fails with Could not determine revocation status.
  3. CertPathValidator.getInstance("PKIX").validate(path, params) checks signatures, validity, basic constraints, key usage and name constraints along the path.
  4. On failure, CertPathValidatorException.getReason() and getIndex() say what went wrong and where: EXPIRED at index 0 for the expired leaf.

Gotchas

  • CertPathValidator validates exactly the list you give it, in that order; it does not search for intermediates. A missing intermediate and a look-alike root both end with Path does not chain with any of the trust anchors (NO_TRUST_ANCHOR, index -1).
  • To find a path among loose certificates, use CertPathBuilder with a CertStore — that is what TLS does, and why TLS errors say "PKIX path building failed".
  • Keep revocation checking on for production trust decisions; turn it off only for CAs that publish no revocation information, as here.

Related recipes