Skip to content
ecdsa.com

Public key from a certificate · Rust

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

To extract the public key from an X.509 certificate in Rust, parse it with x509-parser and take cert.public_key().raw — the SubjectPublicKeyInfo DER exactly as stored — then wrap it as a "PUBLIC KEY" PEM with pem-rfc7468. This recipe also computes the key's SHA-256 fingerprint and base64 pin and checks the result against the private key.

Tested with x509-parser 0.18.1 (Rust 1.99.1, pem-rfc7468 1.0.0, sha2 0.11.1) and OpenSSL 3.5.7 (public half of key.pem for the comparison).

Cargo.toml — x509-parser, pem-rfc7468, sha2 and base64
[dependencies]
x509-parser = "=0.18.1"
pem-rfc7468 = { version = "=1.0.0", features = ["alloc"] }
sha2 = "=0.11.1"
base64 = "=0.23.1"
src/main.rs — SPKI PEM, fingerprint and pin
use base64::Engine;
use base64::engine::general_purpose::STANDARD;
use sha2::{Digest, Sha256};
use x509_parser::pem::parse_x509_pem;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let data = std::fs::read("cert.pem")?;
let (_, pem) = parse_x509_pem(&data)?;
let cert = pem.parse_x509()?;
// .raw: the SubjectPublicKeyInfo DER exactly as the certificate stores it
let spki = cert.public_key().raw;
let spki_pem = pem_rfc7468::encode_string("PUBLIC KEY", pem_rfc7468::LineEnding::LF, spki)?;
std::fs::write("pub.pem", &spki_pem)?;
print!("{spki_pem}");
let digest = Sha256::digest(spki);
let hex: Vec<String> = digest.iter().map(|b| format!("{b:02x}")).collect();
println!("SPKI SHA-256: {}", hex.join(":"));
println!("SPKI pin (base64): {}", STANDARD.encode(digest));
// subject_public_key is only the BIT STRING inside: the bare EC point, not an SPKI
let point = &cert.public_key().subject_public_key.data;
println!("subject_public_key.data: {} bytes, starts with {:02x}", point.len(), point[0]);
Ok(())
}

How it works

  1. parse_x509_pem and parse_x509 give the certificate; cert.public_key() is its SubjectPublicKeyInfo, and .raw the DER bytes of it.
  2. pem_rfc7468::encode_string("PUBLIC KEY", LineEnding::LF, spki) writes the standard PEM; the run checks it equals openssl pkey -in key.pem -pubout.
  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. For verification, hand the SPKI to your crypto crate — for example p256::ecdsa::VerifyingKey::from_public_key_der(spki).

Gotchas

  • public_key().subject_public_key.data is only the BIT STRING inside the SPKI — the bare 65-byte EC point starting with 04 — not the SPKI; hashing it gives neither the fingerprint nor the pin.
  • x509-parser borrows from the input buffer: the raw slice lives as long as the PEM contents, so copy it (to_vec()) before the buffer goes out of scope.
  • The certificate's own hash (over the whole DER) changes at every renewal; the SPKI hash stays the same while the key does.

Related recipes