Skip to content
ecdsa.com

Public key from a certificate · Java

How to extract the public key from an X.509 certificate in Java

To extract the public key from an X.509 certificate in Java, load it with CertificateFactory and call getPublicKey().getEncoded(): the bytes are the SubjectPublicKeyInfo DER, ready to wrap in a "BEGIN PUBLIC KEY" PEM. This recipe does that with the JDK only, computes the key's SHA-256 fingerprint and base64 pin, and checks the result against the private key.

Tested with OpenJDK 25.0.4.1 (Eclipse Temurin 25.0.4.1+1) and OpenSSL 3.0.13 (public half of key.pem for the comparison).

ExtractKey.java — SPKI PEM, fingerprint and pin
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.HexFormat;
public class ExtractKey {
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);
}
PublicKey publicKey = cert.getPublicKey();
byte[] spki = publicKey.getEncoded(); // SubjectPublicKeyInfo DER; getFormat() says "X.509"
String body = Base64.getMimeEncoder(64, "\n".getBytes(StandardCharsets.US_ASCII)).encodeToString(spki);
String pem = "-----BEGIN PUBLIC KEY-----\n" + body + "\n-----END PUBLIC KEY-----\n";
Files.writeString(Path.of("pub.pem"), pem);
System.out.print(pem);
byte[] digest = MessageDigest.getInstance("SHA-256").digest(spki);
System.out.println("SPKI SHA-256: " + HexFormat.ofDelimiter(":").formatHex(digest));
System.out.println("SPKI pin (base64): " + Base64.getEncoder().encodeToString(digest));
System.out.println("algorithm " + publicKey.getAlgorithm() + ", format " + publicKey.getFormat());
}
}

How it works

  1. cert.getPublicKey() returns a PublicKey (ECPublicKey here) that Signature.initVerify accepts directly.
  2. getEncoded() returns the SubjectPublicKeyInfo DER; base64 in 64-character lines between BEGIN PUBLIC KEY and END PUBLIC KEY makes the PEM.
  3. SHA-256 of the SPKI DER is the key fingerprint (45:ca:a7:e3:…:7b:5f), and its base64 (Rcqn43J0…e18=) the SPKI pin used by Android and OkHttp.
  4. The run compares the PEM Java wrote with openssl pkey -in key.pem -pubout: they are identical, so the key belongs to the certificate.

Gotchas

  • getFormat() returns X.509 for this key: in the JCA that names the SubjectPublicKeyInfo structure, not a certificate.
  • cert.getEncoded() is the whole certificate; hashing it gives the certificate fingerprint, which changes at every renewal — pin getPublicKey().getEncoded() instead.
  • Base64.getMimeEncoder() without arguments writes 76-character lines with CRLF; getMimeEncoder(64, "\n") produces exactly what OpenSSL writes, which the run's byte comparison confirms.

Related recipes