import java.io.InputStream;import java.nio.file.Files;import java.nio.file.Path;import java.security.AlgorithmParameters;import java.security.MessageDigest;import java.security.cert.CertificateFactory;import java.security.cert.X509Certificate;import java.security.interfaces.ECPublicKey;import java.security.spec.ECGenParameterSpec;import java.time.Duration;import java.time.Instant;import java.util.HexFormat;import java.util.List; public class DecodeCert { public static void main(String[] args) throws Exception { X509Certificate cert; try (InputStream in = Files.newInputStream(Path.of("cert.pem"))) { // PEM or DER cert = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(in); } System.out.println("subject: " + cert.getSubjectX500Principal().getName()); System.out.println("issuer: " + cert.getIssuerX500Principal().getName()); System.out.println("serial: " + cert.getSerialNumber().toString(16)); System.out.println("valid: " + cert.getNotBefore().toInstant() + " -> " + cert.getNotAfter().toInstant()); System.out.println("signed with: " + cert.getSigAlgName()); // the issuer's algorithm if (cert.getSubjectAlternativeNames() != null) { for (List<?> name : cert.getSubjectAlternativeNames()) { if ((Integer) name.get(0) == 2) System.out.println("DNS name: " + name.get(1)); // 2 = dNSName } } if (cert.getPublicKey() instanceof ECPublicKey ecKey) { AlgorithmParameters params = AlgorithmParameters.getInstance("EC"); params.init(ecKey.getParams()); System.out.println("key: EC " + params.getParameterSpec(ECGenParameterSpec.class).getName()); } byte[] sha256 = MessageDigest.getInstance("SHA-256").digest(cert.getEncoded()); // the whole DER certificate System.out.println("SHA-256 fingerprint: " + HexFormat.ofDelimiter(":").withUpperCase().formatHex(sha256)); System.out.println("days until expiry: " + Duration.between(Instant.now(), cert.getNotAfter().toInstant()).toDays()); }} // A bundle, leaf first: parse every block and check who signed whomList<X509Certificate> chain;try (InputStream in = Files.newInputStream(Path.of("chain.pem"))) { chain = CertificateFactory.getInstance("X.509").generateCertificates(in).stream() .map(X509Certificate.class::cast) .toList();}for (int i = 0; i < chain.size(); i++) { X509Certificate cert = chain.get(i); X509Certificate issuer = chain.get(Math.min(i + 1, chain.size() - 1)); // the root signs itself cert.verify(issuer.getPublicKey()); // SignatureException unless this issuer signed it System.out.println(i + ": " + cert.getSubjectX500Principal().getName() + " signed by " + issuer.getSubjectX500Principal().getName());} How it works
CertificateFactory.getInstance("X.509").generateCertificatereads PEM and binary DER alike — the run above feeds it both forms of the same certificate.getSubjectX500Principal().getName()returns the RFC 2253 string; validity dates come back asjava.util.Date, andtoInstant()gives exact UTC timestamps for comparisons.getSubjectAlternativeNames()returns a list of[type, value]pairs per name; type2is a DNS name and type7an IP address. It returnsnull, not an empty list, when the extension is absent.- The curve hides in the key parameters:
AlgorithmParametersconverts the key'sECParameterSpecback to the standard name,secp256r1for P-256. generateCertificates(plural) parses every block of a bundle, andcert.verify(issuerKey)throws unless that key produced the certificate's signature.
Gotchas
- Java prints distinguished names in RFC 2253 order, the reverse of OpenSSL: this certificate is
C=US,O=…,CN=demo.ecdsa.comhere andCN=demo.ecdsa.com, O=…, C=USinopenssl x509 -subject. CompareX500Principalobjects, not strings. getSerialNumber().toString(16)drops leading zeros (4a1b2c3d4e5f607, where OpenSSL prints04A1B2C3D4E5F607). Normalize the format before matching serials from different tools.getSigAlgName()names the issuer's signature over this certificate, not what the certificate's own key can do — an ECDSA leaf issued by an RSA CA reportsSHA256withRSA.- Decoding is not validation: the chain loop proves who signed whom, nothing more. Trust needs
CertPathValidatorwith your trust anchors, andcheckValidity()throwsCertificateExpiredExceptiononcenotAfterhas passed.