use crate::common::error::FlareError;
use crate::common::error::Result;
use rustls_pki_types::pem::PemObject;
pub fn pem_cert_to_der(pem_data: &[u8]) -> Result<Vec<u8>> {
use rustls_pki_types::CertificateDer;
match CertificateDer::pem_slice_iter(pem_data).next() {
Some(Ok(cert)) => Ok(cert.to_vec()),
Some(Err(e)) => Err(FlareError::protocol_error(format!(
"Failed to parse PEM certificate: {}",
e
))),
None => Err(FlareError::protocol_error(
"No certificates found in PEM data".to_string(),
)),
}
}
pub fn pem_key_to_der(pem_data: &[u8]) -> Result<Vec<u8>> {
use rustls_pki_types::PrivateKeyDer;
match PrivateKeyDer::from_pem_slice(pem_data) {
Ok(PrivateKeyDer::Pkcs8(key)) => Ok(key.secret_pkcs8_der().to_vec()),
Ok(PrivateKeyDer::Sec1(key)) => Ok(key.secret_sec1_der().to_vec()),
Ok(_) => Err(FlareError::protocol_error(
"Unsupported private key encoding in PEM data (expected PKCS#8 or SEC1)".to_string(),
)),
Err(e) => Err(FlareError::protocol_error(format!(
"No private key found in PEM data: {}",
e
))),
}
}
pub fn der_cert_to_pem(der_data: &[u8]) -> String {
use base64::Engine;
let base64_cert = base64::engine::general_purpose::STANDARD.encode(der_data);
let formatted = base64_cert
.as_bytes()
.chunks(64)
.map(|chunk| String::from_utf8_lossy(chunk))
.collect::<Vec<_>>()
.join("\n");
format!(
"-----BEGIN CERTIFICATE-----\n{}\n-----END CERTIFICATE-----\n",
formatted
)
}
pub fn der_key_to_pem(der_data: &[u8]) -> String {
use base64::Engine;
let base64_key = base64::engine::general_purpose::STANDARD.encode(der_data);
let formatted = base64_key
.as_bytes()
.chunks(64)
.map(|chunk| String::from_utf8_lossy(chunk))
.collect::<Vec<_>>()
.join("\n");
format!(
"-----BEGIN PRIVATE KEY-----\n{}\n-----END PRIVATE KEY-----\n",
formatted
)
}
#[cfg(test)]
mod tests {
use super::*;
const CERT_PEM: &[u8] = include_bytes!("testdata/cert.pem");
const PKCS8_PEM: &[u8] = include_bytes!("testdata/k8.pem");
const SEC1_PEM: &[u8] = include_bytes!("testdata/sec1.pem");
const PKCS1_PEM: &[u8] = include_bytes!("testdata/pkcs1.pem");
#[test]
fn parses_pem_certificate_to_der() {
let der = pem_cert_to_der(CERT_PEM).expect("应能解析自签证书");
assert_eq!(der[0], 0x30, "证书 DER 应以 SEQUENCE 开头");
assert!(der.len() > 100);
}
#[test]
fn parses_pkcs8_and_sec1_keys() {
for (name, pem) in [("PKCS#8", PKCS8_PEM), ("SEC1", SEC1_PEM)] {
let der = pem_key_to_der(pem).unwrap_or_else(|e| panic!("{name} 应能解析: {e}"));
assert_eq!(der[0], 0x30, "{name} 私钥 DER 应以 SEQUENCE 开头");
}
}
#[test]
fn rejects_pkcs1_key_as_before() {
let err = pem_key_to_der(PKCS1_PEM).expect_err("PKCS#1 应被拒绝");
assert!(
err.to_string().contains("PKCS#8 or SEC1"),
"错误应说明期望的编码,实际: {err}"
);
}
#[test]
fn rejects_input_without_pem_blocks() {
assert!(pem_cert_to_der(b"not a pem at all").is_err());
assert!(pem_key_to_der(b"not a pem at all").is_err());
}
}