[dependencies]pem-rfc7468 = { version = "=1.0.0", features = ["alloc"] }sha2 = "=0.11.1"hex = "=0.4.3" use pem_rfc7468::LineEnding; /// The label ("CERTIFICATE", "PRIVATE KEY", ...) and the DER bytes of a PEM document.pub fn pem_to_der(pem: &str) -> Result<(String, Vec<u8>), pem_rfc7468::Error> { let (label, der) = pem_rfc7468::decode_vec(pem.as_bytes())?; Ok((label.to_owned(), der))} /// DER bytes as PEM: base64 in 64-character lines between BEGIN and END.pub fn der_to_pem(label: &str, der: &[u8]) -> Result<String, pem_rfc7468::Error> { pem_rfc7468::encode_string(label, LineEnding::LF, der)} How it works
pem_rfc7468::decode_vecreturns(label, der):CERTIFICATEand 563 bytes for the certificate,PRIVATE KEYand 138 bytes for the PKCS#8 key.encode_string(label, LineEnding::LF, der)writes 64-character lines; the round trip reproduces the original files exactly.- Parsers take the DER:
x509-parser'sparse_x509_certificate, or thepkcs8crate'sDecodePrivateKey::from_pkcs8_derfor keys. - The run checks the DER with OpenSSL as well (
openssl x509 -inform DER,openssl pkey -inform DER).
Gotchas
- The decoder follows RFC 7468's strict form: a PEM re-wrapped at 76 characters (as
base64does) fails withBase64(InvalidEncoding). Normalise such files withopenssl x509first. decode_vectakes exactly one document: a two-certificate bundle also fails withBase64(InvalidEncoding). Split bundles before decoding.- Legacy encrypted PEM (
Proc-Type: 4,ENCRYPTEDheaders, asopenssl ec -aes128writes) is rejected withHeaderDisallowed; convert it to encrypted PKCS#8 (openssl pkcs8 -topk8).
Related recipes
- How to convert a certificate and a private key between PEM and DER in Go
- How to convert a certificate and a private key between PEM and DER in Python
- How to convert a certificate and a private key between PEM and DER with OpenSSL
- How to export a public key as PEM in Rust
- How to decode an X.509 certificate in Rust