Skip to main content

jks/
pkcs12.rs

1// Copyright (c) 2024 Keystore-RS Contributors
2// SPDX-License-Identifier: MIT
3
4//! PKCS12 keystore support using pure Rust p12-keystore library
5//!
6//! This module provides support for reading PKCS12 formatted keystores.
7//! PKCS12 is the successor to JKS and is the default format for Android keystores.
8
9use crate::{Certificate, Entry, KeyStore, KeyStoreError, PrivateKeyEntry, Result};
10use std::io::Read;
11
12/// PKCS12 magic bytes (ASN.1 SEQUENCE tag = 0x30)
13pub const PKCS12_MAGIC: u8 = 0x30;
14
15/// Detect if data is PKCS12 format
16pub fn is_pkcs12_data(data: &[u8]) -> bool {
17    !data.is_empty() && data[0] == PKCS12_MAGIC
18}
19
20impl KeyStore {
21    /// Load a PKCS12 keystore from reader
22    ///
23    /// PKCS12 is the standard keystore format used by:
24    /// - Android (`.keystore` files)
25    /// - Java (`.p12`/`.pfx` files)
26    /// - OpenSSL
27    pub fn load_pkcs12<R: Read>(&mut self, mut reader: R, password: &[u8]) -> Result<()> {
28        #[cfg(feature = "pkcs12")]
29        {
30            use p12_keystore::{KeyStore as P12KeyStore, KeyStoreEntry as P12Entry};
31
32            let mut buffer = Vec::new();
33            reader.read_to_end(&mut buffer)?;
34
35            let password_str = std::str::from_utf8(password)
36                .map_err(|_| KeyStoreError::Other("Invalid UTF-8 password".to_string()))?;
37
38            // Parse the PKCS12 structure using p12-keystore
39            let p12_ks = P12KeyStore::from_pkcs12(&buffer, password_str)
40                .map_err(|e| KeyStoreError::Other(format!("PKCS12 parse error: {}", e)))?;
41
42            // Clear existing entries
43            self.entries.clear();
44
45            // Process all entries
46            for (alias, entry) in p12_ks.entries() {
47                match entry {
48                    P12Entry::PrivateKeyChain(chain) => {
49                        // Get the private key in PKCS#8 DER format
50                        let private_key = chain.key().as_der().to_vec();
51
52                        // Build certificate chain
53                        let cert_chain: Vec<Certificate> = chain
54                            .certs()
55                            .iter()
56                            .map(|cert| Certificate {
57                                cert_type: "X509".to_string(),
58                                content: cert.as_der().to_vec(),
59                            })
60                            .collect();
61
62                        let entry = PrivateKeyEntry {
63                            // Use UNIX_EPOCH for WASM compatibility (SystemTime::now() panics in WASM)
64                            creation_time: std::time::SystemTime::UNIX_EPOCH,
65                            private_key,
66                            certificate_chain: cert_chain,
67                        };
68
69                        self.entries
70                            .insert(self.convert_alias(alias), Entry::PrivateKey(entry));
71                    }
72                    P12Entry::Certificate(cert) => {
73                        // Trusted certificate entry
74                        let tce = crate::TrustedCertificateEntry {
75                            // Use UNIX_EPOCH for WASM compatibility (SystemTime::now() panics in WASM)
76                            creation_time: std::time::SystemTime::UNIX_EPOCH,
77                            certificate: Certificate {
78                                cert_type: "X509".to_string(),
79                                content: cert.as_der().to_vec(),
80                            },
81                        };
82                        self.entries
83                            .insert(self.convert_alias(alias), Entry::TrustedCertificate(tce));
84                    }
85                    P12Entry::Secret(_) => {
86                        // Secret entries are not supported in JKS format, skip
87                    }
88                }
89            }
90
91            Ok(())
92        }
93
94        #[cfg(not(feature = "pkcs12"))]
95        {
96            let _ = (reader, password);
97            Err(KeyStoreError::Other(
98                "PKCS12 feature not enabled. Enable with: cargo build --features pkcs12"
99                    .to_string(),
100            ))
101        }
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_is_pkcs12_data() {
111        // PKCS12 starts with ASN.1 SEQUENCE tag (0x30)
112        assert!(is_pkcs12_data(&[0x30, 0x82, 0x00, 0x00]));
113        assert!(!is_pkcs12_data(&[0xFE, 0xED, 0xFE, 0xED])); // JKS magic
114    }
115}
116
117#[cfg(all(test, feature = "pkcs12"))]
118mod integration_tests {
119    use super::*;
120    use std::io::Cursor;
121
122    #[test]
123    fn test_load_pbes2_keystore() {
124        let data = include_bytes!("../p12-keystore-main/tests/assets/pbes2-keystore.p12");
125        let mut ks = KeyStore::new();
126        ks.load_pkcs12(Cursor::new(data.as_slice()), b"changeit").unwrap();
127        
128        // Should have at least one entry
129        assert!(!ks.is_empty(), "Keystore should not be empty");
130        
131        // Check that we can access the entries
132        for alias in ks.aliases() {
133            if ks.is_private_key_entry(&alias) {
134                let entry = ks.get_raw_private_key_entry(&alias).unwrap();
135                assert!(!entry.private_key.is_empty());
136                assert!(!entry.certificate_chain.is_empty());
137            }
138        }
139    }
140    
141    #[test]
142    fn test_load_auto_detect_pkcs12() {
143        let data = include_bytes!("../p12-keystore-main/tests/assets/pbes2-keystore.p12");
144        let mut ks = KeyStore::new();
145        ks.load_auto_detect(Cursor::new(data.as_slice()), b"changeit").unwrap();
146        
147        assert!(!ks.is_empty(), "Keystore should not be empty after auto-detect load");
148    }
149}