use std::error::Error; use std::io::{Cursor, ErrorKind}; use rustls::{Certificate, PrivateKey}; use rustls_pemfile::{read_one, Item}; /// Parse PEM certificates bytes into a [`rustls::Certificate`] structure /// /// An error is returned if not any certificate could be found pub fn parse_pem_certificates(certs: &[u8]) -> Result, Box> { let certs = rustls_pemfile::certs(&mut Cursor::new(certs))? .into_iter() .map(Certificate) .collect::>(); if certs.is_empty() { Err(std::io::Error::new( ErrorKind::InvalidData, "Could not find any certificate!", ))?; unreachable!(); } Ok(certs) } /// Parse PEM private key bytes into a [`rustls::PrivateKey`] structure pub fn parse_pem_private_key(privkey: &[u8]) -> Result> { let key = match read_one(&mut Cursor::new(privkey))? { None => { Err(std::io::Error::new( ErrorKind::Other, "Failed to extract private key!", ))?; unreachable!() } Some(Item::PKCS8Key(key)) => key, Some(Item::RSAKey(key)) => key, _ => { Err(std::io::Error::new( ErrorKind::Other, "Unsupported private key type!", ))?; unreachable!(); } }; Ok(PrivateKey(key)) } #[cfg(test)] mod test { use crate::cert_utils::{parse_pem_certificates, parse_pem_private_key}; const SAMPLE_CERT: &[u8] = include_bytes!("../samples/TCPTunnelTest.crt"); const SAMPLE_KEY: &[u8] = include_bytes!("../samples/TCPTunnelTest.key"); #[test] fn parse_valid_cert() { parse_pem_certificates(SAMPLE_CERT).unwrap(); } #[test] fn parse_invalid_cert_1() { parse_pem_certificates("Random content".as_bytes()).unwrap_err(); } #[test] fn parse_invalid_cert_2() { parse_pem_certificates(SAMPLE_KEY).unwrap_err(); } #[test] fn parse_valid_key() { parse_pem_private_key(SAMPLE_KEY).unwrap(); } #[test] fn parse_invalid_key_1() { parse_pem_private_key("Random content".as_bytes()).unwrap_err(); } #[test] fn parse_invalid_key_2() { parse_pem_private_key(SAMPLE_CERT).unwrap_err(); } }