use crate::{Certificate, Entry, KeyStore, KeyStoreError, PrivateKeyEntry, Result};
use std::io::Read;
pub const PKCS12_MAGIC: u8 = 0x30;
pub fn is_pkcs12_data(data: &[u8]) -> bool {
!data.is_empty() && data[0] == PKCS12_MAGIC
}
impl KeyStore {
pub fn load_pkcs12<R: Read>(&mut self, mut reader: R, password: &[u8]) -> Result<()> {
#[cfg(feature = "pkcs12")]
{
use p12_keystore::{KeyStore as P12KeyStore, KeyStoreEntry as P12Entry};
let mut buffer = Vec::new();
reader.read_to_end(&mut buffer)?;
let password_str = std::str::from_utf8(password)
.map_err(|_| KeyStoreError::Other("Invalid UTF-8 password".to_string()))?;
let p12_ks = P12KeyStore::from_pkcs12(&buffer, password_str)
.map_err(|e| KeyStoreError::Other(format!("PKCS12 parse error: {}", e)))?;
self.entries.clear();
for (alias, entry) in p12_ks.entries() {
match entry {
P12Entry::PrivateKeyChain(chain) => {
let private_key = chain.key().as_der().to_vec();
let cert_chain: Vec<Certificate> = chain
.certs()
.iter()
.map(|cert| Certificate {
cert_type: "X509".to_string(),
content: cert.as_der().to_vec(),
})
.collect();
let entry = PrivateKeyEntry {
creation_time: std::time::SystemTime::UNIX_EPOCH,
private_key,
certificate_chain: cert_chain,
};
self.entries
.insert(self.convert_alias(alias), Entry::PrivateKey(entry));
}
P12Entry::Certificate(cert) => {
let tce = crate::TrustedCertificateEntry {
creation_time: std::time::SystemTime::UNIX_EPOCH,
certificate: Certificate {
cert_type: "X509".to_string(),
content: cert.as_der().to_vec(),
},
};
self.entries
.insert(self.convert_alias(alias), Entry::TrustedCertificate(tce));
}
P12Entry::Secret(_) => {
}
}
}
Ok(())
}
#[cfg(not(feature = "pkcs12"))]
{
let _ = (reader, password);
Err(KeyStoreError::Other(
"PKCS12 feature not enabled. Enable with: cargo build --features pkcs12"
.to_string(),
))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_pkcs12_data() {
assert!(is_pkcs12_data(&[0x30, 0x82, 0x00, 0x00]));
assert!(!is_pkcs12_data(&[0xFE, 0xED, 0xFE, 0xED])); }
}
#[cfg(all(test, feature = "pkcs12"))]
mod integration_tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_load_pbes2_keystore() {
let data = include_bytes!("../p12-keystore-main/tests/assets/pbes2-keystore.p12");
let mut ks = KeyStore::new();
ks.load_pkcs12(Cursor::new(data.as_slice()), b"changeit").unwrap();
assert!(!ks.is_empty(), "Keystore should not be empty");
for alias in ks.aliases() {
if ks.is_private_key_entry(&alias) {
let entry = ks.get_raw_private_key_entry(&alias).unwrap();
assert!(!entry.private_key.is_empty());
assert!(!entry.certificate_chain.is_empty());
}
}
}
#[test]
fn test_load_auto_detect_pkcs12() {
let data = include_bytes!("../p12-keystore-main/tests/assets/pbes2-keystore.p12");
let mut ks = KeyStore::new();
ks.load_auto_detect(Cursor::new(data.as_slice()), b"changeit").unwrap();
assert!(!ks.is_empty(), "Keystore should not be empty after auto-detect load");
}
}