Skip to content
ecdsa.com

Public key from a certificate · Kotlin

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

To extract the public key from an X.509 certificate in Kotlin, parse it with the JDK's CertificateFactory and take publicKey.encoded — the SubjectPublicKeyInfo DER — then wrap it as a "BEGIN PUBLIC KEY" PEM. This recipe adds an extension function for the PEM, computes the key's SHA-256 fingerprint and base64 pin, and checks the result against the private key.

Tested with Kotlin 2.4.21 (Kotlin/JVM, OpenJDK 25.0.4.1 (Temurin), Gradle 9.8.1) and OpenSSL 3.0.13 (public half of key.pem for the comparison).

Main.kt — SPKI PEM, fingerprint and pin
import java.security.MessageDigest
import java.security.PublicKey
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.util.Base64
import kotlin.io.path.Path
import kotlin.io.path.inputStream
/** SubjectPublicKeyInfo as PEM: base64 of the DER in 64-character lines. */
fun PublicKey.toSpkiPem(): String =
"-----BEGIN PUBLIC KEY-----\n" +
Base64.getEncoder().encodeToString(encoded).chunked(64).joinToString("\n") +
"\n-----END PUBLIC KEY-----\n"
fun main() {
val cert = Path("cert.pem").inputStream().use {
CertificateFactory.getInstance("X.509").generateCertificate(it) as X509Certificate
}
val publicKey = cert.publicKey
print(publicKey.toSpkiPem())
val digest = MessageDigest.getInstance("SHA-256").digest(publicKey.encoded) // encoded = SPKI DER
println("SPKI SHA-256: ${digest.toHexString(HexFormat { bytes.byteSeparator = ":" })}")
println("SPKI pin (base64): ${Base64.getEncoder().encodeToString(digest)}")
println("algorithm ${publicKey.algorithm}, format ${publicKey.format}") // "X.509" means SPKI here
}

How it works

  1. CertificateFactory.getInstance("X.509").generateCertificate parses PEM or DER; cert.publicKey is the key, usable with Signature.initVerify.
  2. publicKey.encoded is the SubjectPublicKeyInfo DER; toSpkiPem() chunks its base64 into 64-character lines between the PEM markers.
  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.
  4. The run compares the printed PEM with openssl pkey -in key.pem -pubout: identical, so the private key belongs to the certificate.

Gotchas

  • publicKey.format is X.509 — the JCA's name for the SubjectPublicKeyInfo structure, not a sign that you are holding a certificate.
  • cert.encoded is the whole certificate; its hash is the certificate fingerprint, which changes at every renewal, while the SPKI hash survives renewals with the same key.
  • Base64.getMimeEncoder() with no arguments would write 76-character lines with CRLF; chunking the plain encoder's output at 64 matches OpenSSL byte for byte.

Related recipes