1 | //! PKCS #12 archives. |
2 | |
3 | use foreign_types::{ForeignType, ForeignTypeRef}; |
4 | use libc::c_int; |
5 | use std::ffi::CString; |
6 | use std::ptr; |
7 | |
8 | use crate::error::ErrorStack; |
9 | #[cfg (not(boringssl))] |
10 | use crate::hash::MessageDigest; |
11 | use crate::nid::Nid; |
12 | use crate::pkey::{HasPrivate, PKey, PKeyRef, Private}; |
13 | use crate::stack::Stack; |
14 | use crate::util::ForeignTypeExt; |
15 | use crate::x509::{X509Ref, X509}; |
16 | use crate::{cvt, cvt_p}; |
17 | use openssl_macros::corresponds; |
18 | |
19 | foreign_type_and_impl_send_sync! { |
20 | type CType = ffi::PKCS12; |
21 | fn drop = ffi::PKCS12_free; |
22 | |
23 | pub struct Pkcs12; |
24 | pub struct Pkcs12Ref; |
25 | } |
26 | |
27 | impl Pkcs12Ref { |
28 | to_der! { |
29 | /// Serializes the `Pkcs12` to its standard DER encoding. |
30 | #[corresponds (i2d_PKCS12)] |
31 | to_der, |
32 | ffi::i2d_PKCS12 |
33 | } |
34 | |
35 | /// Deprecated. |
36 | #[deprecated (note = "Use parse2 instead" , since = "0.10.46" )] |
37 | #[allow (deprecated)] |
38 | pub fn parse(&self, pass: &str) -> Result<ParsedPkcs12, ErrorStack> { |
39 | let parsed = self.parse2(pass)?; |
40 | |
41 | Ok(ParsedPkcs12 { |
42 | pkey: parsed.pkey.unwrap(), |
43 | cert: parsed.cert.unwrap(), |
44 | chain: parsed.ca, |
45 | }) |
46 | } |
47 | |
48 | /// Extracts the contents of the `Pkcs12`. |
49 | #[corresponds (PKCS12_parse)] |
50 | pub fn parse2(&self, pass: &str) -> Result<ParsedPkcs12_2, ErrorStack> { |
51 | unsafe { |
52 | let pass = CString::new(pass.as_bytes()).unwrap(); |
53 | |
54 | let mut pkey = ptr::null_mut(); |
55 | let mut cert = ptr::null_mut(); |
56 | let mut ca = ptr::null_mut(); |
57 | |
58 | cvt(ffi::PKCS12_parse( |
59 | self.as_ptr(), |
60 | pass.as_ptr(), |
61 | &mut pkey, |
62 | &mut cert, |
63 | &mut ca, |
64 | ))?; |
65 | |
66 | let pkey = PKey::from_ptr_opt(pkey); |
67 | let cert = X509::from_ptr_opt(cert); |
68 | let ca = Stack::from_ptr_opt(ca); |
69 | |
70 | Ok(ParsedPkcs12_2 { pkey, cert, ca }) |
71 | } |
72 | } |
73 | } |
74 | |
75 | impl Pkcs12 { |
76 | from_der! { |
77 | /// Deserializes a DER-encoded PKCS#12 archive. |
78 | #[corresponds (d2i_PKCS12)] |
79 | from_der, |
80 | Pkcs12, |
81 | ffi::d2i_PKCS12 |
82 | } |
83 | |
84 | /// Creates a new builder for a protected pkcs12 certificate. |
85 | /// |
86 | /// This uses the defaults from the OpenSSL library: |
87 | /// |
88 | /// * `nid_key` - `AES_256_CBC` (3.0.0+) or `PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC` |
89 | /// * `nid_cert` - `AES_256_CBC` (3.0.0+) or `PBE_WITHSHA1AND40BITRC2_CBC` |
90 | /// * `iter` - `2048` |
91 | /// * `mac_iter` - `2048` |
92 | /// * `mac_md` - `SHA-256` (3.0.0+) or `SHA-1` (`SHA-1` only for BoringSSL) |
93 | pub fn builder() -> Pkcs12Builder { |
94 | ffi::init(); |
95 | |
96 | Pkcs12Builder { |
97 | name: None, |
98 | pkey: None, |
99 | cert: None, |
100 | ca: None, |
101 | nid_key: Nid::UNDEF, |
102 | nid_cert: Nid::UNDEF, |
103 | iter: ffi::PKCS12_DEFAULT_ITER, |
104 | mac_iter: ffi::PKCS12_DEFAULT_ITER, |
105 | #[cfg (not(boringssl))] |
106 | mac_md: None, |
107 | } |
108 | } |
109 | } |
110 | |
111 | #[deprecated (note = "Use ParsedPkcs12_2 instead" , since = "0.10.46" )] |
112 | pub struct ParsedPkcs12 { |
113 | pub pkey: PKey<Private>, |
114 | pub cert: X509, |
115 | pub chain: Option<Stack<X509>>, |
116 | } |
117 | |
118 | pub struct ParsedPkcs12_2 { |
119 | pub pkey: Option<PKey<Private>>, |
120 | pub cert: Option<X509>, |
121 | pub ca: Option<Stack<X509>>, |
122 | } |
123 | |
124 | pub struct Pkcs12Builder { |
125 | // FIXME borrow |
126 | name: Option<CString>, |
127 | pkey: Option<PKey<Private>>, |
128 | cert: Option<X509>, |
129 | ca: Option<Stack<X509>>, |
130 | nid_key: Nid, |
131 | nid_cert: Nid, |
132 | iter: c_int, |
133 | mac_iter: c_int, |
134 | // FIXME remove |
135 | #[cfg (not(boringssl))] |
136 | mac_md: Option<MessageDigest>, |
137 | } |
138 | |
139 | impl Pkcs12Builder { |
140 | /// The `friendlyName` used for the certificate and private key. |
141 | pub fn name(&mut self, name: &str) -> &mut Self { |
142 | self.name = Some(CString::new(name).unwrap()); |
143 | self |
144 | } |
145 | |
146 | /// The private key. |
147 | pub fn pkey<T>(&mut self, pkey: &PKeyRef<T>) -> &mut Self |
148 | where |
149 | T: HasPrivate, |
150 | { |
151 | let new_pkey = unsafe { PKeyRef::from_ptr(pkey.as_ptr()) }; |
152 | self.pkey = Some(new_pkey.to_owned()); |
153 | self |
154 | } |
155 | |
156 | /// The certificate. |
157 | pub fn cert(&mut self, cert: &X509Ref) -> &mut Self { |
158 | self.cert = Some(cert.to_owned()); |
159 | self |
160 | } |
161 | |
162 | /// An additional set of certificates to include in the archive beyond the one provided to |
163 | /// `build`. |
164 | pub fn ca(&mut self, ca: Stack<X509>) -> &mut Self { |
165 | self.ca = Some(ca); |
166 | self |
167 | } |
168 | |
169 | /// The encryption algorithm that should be used for the key |
170 | pub fn key_algorithm(&mut self, nid: Nid) -> &mut Self { |
171 | self.nid_key = nid; |
172 | self |
173 | } |
174 | |
175 | /// The encryption algorithm that should be used for the cert |
176 | pub fn cert_algorithm(&mut self, nid: Nid) -> &mut Self { |
177 | self.nid_cert = nid; |
178 | self |
179 | } |
180 | |
181 | /// Key iteration count, default is 2048 as of this writing |
182 | pub fn key_iter(&mut self, iter: u32) -> &mut Self { |
183 | self.iter = iter as c_int; |
184 | self |
185 | } |
186 | |
187 | /// MAC iteration count, default is the same as key_iter. |
188 | /// |
189 | /// Old implementations don't understand MAC iterations greater than 1, (pre 1.0.1?), if such |
190 | /// compatibility is required this should be set to 1. |
191 | pub fn mac_iter(&mut self, mac_iter: u32) -> &mut Self { |
192 | self.mac_iter = mac_iter as c_int; |
193 | self |
194 | } |
195 | |
196 | /// MAC message digest type |
197 | #[cfg (not(boringssl))] |
198 | pub fn mac_md(&mut self, md: MessageDigest) -> &mut Self { |
199 | self.mac_md = Some(md); |
200 | self |
201 | } |
202 | |
203 | /// Deprecated. |
204 | #[deprecated ( |
205 | note = "Use Self::{name, pkey, cert, build2} instead." , |
206 | since = "0.10.46" |
207 | )] |
208 | pub fn build<T>( |
209 | mut self, |
210 | password: &str, |
211 | friendly_name: &str, |
212 | pkey: &PKeyRef<T>, |
213 | cert: &X509Ref, |
214 | ) -> Result<Pkcs12, ErrorStack> |
215 | where |
216 | T: HasPrivate, |
217 | { |
218 | self.name(friendly_name) |
219 | .pkey(pkey) |
220 | .cert(cert) |
221 | .build2(password) |
222 | } |
223 | |
224 | /// Builds the PKCS#12 object. |
225 | #[corresponds (PKCS12_create)] |
226 | pub fn build2(&self, password: &str) -> Result<Pkcs12, ErrorStack> { |
227 | unsafe { |
228 | let pass = CString::new(password).unwrap(); |
229 | let pass = pass.as_ptr(); |
230 | let friendly_name = self.name.as_ref().map_or(ptr::null(), |p| p.as_ptr()); |
231 | let pkey = self.pkey.as_ref().map_or(ptr::null(), |p| p.as_ptr()); |
232 | let cert = self.cert.as_ref().map_or(ptr::null(), |p| p.as_ptr()); |
233 | let ca = self |
234 | .ca |
235 | .as_ref() |
236 | .map(|ca| ca.as_ptr()) |
237 | .unwrap_or(ptr::null_mut()); |
238 | let nid_key = self.nid_key.as_raw(); |
239 | let nid_cert = self.nid_cert.as_raw(); |
240 | |
241 | // According to the OpenSSL docs, keytype is a non-standard extension for MSIE, |
242 | // It's values are KEY_SIG or KEY_EX, see the OpenSSL docs for more information: |
243 | // https://www.openssl.org/docs/manmaster/crypto/PKCS12_create.html |
244 | let keytype = 0; |
245 | |
246 | let pkcs12 = cvt_p(ffi::PKCS12_create( |
247 | pass as *mut _, |
248 | friendly_name as *mut _, |
249 | pkey as *mut _, |
250 | cert as *mut _, |
251 | ca, |
252 | nid_key, |
253 | nid_cert, |
254 | self.iter, |
255 | self.mac_iter, |
256 | keytype, |
257 | )) |
258 | .map(Pkcs12)?; |
259 | |
260 | #[cfg (not(boringssl))] |
261 | // BoringSSL does not support overriding the MAC and will always |
262 | // use SHA-1 |
263 | { |
264 | let md_type = self |
265 | .mac_md |
266 | .map(|md_type| md_type.as_ptr()) |
267 | .unwrap_or(ptr::null()); |
268 | |
269 | cvt(ffi::PKCS12_set_mac( |
270 | pkcs12.as_ptr(), |
271 | pass, |
272 | -1, |
273 | ptr::null_mut(), |
274 | 0, |
275 | self.mac_iter, |
276 | md_type, |
277 | ))?; |
278 | } |
279 | |
280 | Ok(pkcs12) |
281 | } |
282 | } |
283 | } |
284 | |
285 | #[cfg (test)] |
286 | mod test { |
287 | use crate::asn1::Asn1Time; |
288 | use crate::hash::MessageDigest; |
289 | use crate::nid::Nid; |
290 | use crate::pkey::PKey; |
291 | use crate::rsa::Rsa; |
292 | use crate::x509::extension::KeyUsage; |
293 | use crate::x509::{X509Name, X509}; |
294 | |
295 | use super::*; |
296 | |
297 | #[test ] |
298 | fn parse() { |
299 | #[cfg (ossl300)] |
300 | let _provider = crate::provider::Provider::try_load(None, "legacy" , true).unwrap(); |
301 | |
302 | let der = include_bytes!("../test/identity.p12" ); |
303 | let pkcs12 = Pkcs12::from_der(der).unwrap(); |
304 | let parsed = pkcs12.parse2("mypass" ).unwrap(); |
305 | |
306 | assert_eq!( |
307 | hex::encode( |
308 | parsed |
309 | .cert |
310 | .as_ref() |
311 | .unwrap() |
312 | .digest(MessageDigest::sha1()) |
313 | .unwrap() |
314 | ), |
315 | "59172d9313e84459bcff27f967e79e6e9217e584" |
316 | ); |
317 | assert_eq!( |
318 | parsed.cert.as_ref().unwrap().alias(), |
319 | Some(b"foobar.com" as &[u8]) |
320 | ); |
321 | |
322 | let chain = parsed.ca.unwrap(); |
323 | assert_eq!(chain.len(), 1); |
324 | assert_eq!( |
325 | hex::encode(chain[0].digest(MessageDigest::sha1()).unwrap()), |
326 | "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875" |
327 | ); |
328 | } |
329 | |
330 | #[test ] |
331 | fn parse_empty_chain() { |
332 | #[cfg (ossl300)] |
333 | let _provider = crate::provider::Provider::try_load(None, "legacy" , true).unwrap(); |
334 | |
335 | let der = include_bytes!("../test/keystore-empty-chain.p12" ); |
336 | let pkcs12 = Pkcs12::from_der(der).unwrap(); |
337 | let parsed = pkcs12.parse2("cassandra" ).unwrap(); |
338 | if let Some(stack) = parsed.ca { |
339 | assert_eq!(stack.len(), 0); |
340 | } |
341 | } |
342 | |
343 | #[test ] |
344 | fn create() { |
345 | let subject_name = "ns.example.com" ; |
346 | let rsa = Rsa::generate(2048).unwrap(); |
347 | let pkey = PKey::from_rsa(rsa).unwrap(); |
348 | |
349 | let mut name = X509Name::builder().unwrap(); |
350 | name.append_entry_by_nid(Nid::COMMONNAME, subject_name) |
351 | .unwrap(); |
352 | let name = name.build(); |
353 | |
354 | let key_usage = KeyUsage::new().digital_signature().build().unwrap(); |
355 | |
356 | let mut builder = X509::builder().unwrap(); |
357 | builder.set_version(2).unwrap(); |
358 | builder |
359 | .set_not_before(&Asn1Time::days_from_now(0).unwrap()) |
360 | .unwrap(); |
361 | builder |
362 | .set_not_after(&Asn1Time::days_from_now(365).unwrap()) |
363 | .unwrap(); |
364 | builder.set_subject_name(&name).unwrap(); |
365 | builder.set_issuer_name(&name).unwrap(); |
366 | builder.append_extension(key_usage).unwrap(); |
367 | builder.set_pubkey(&pkey).unwrap(); |
368 | builder.sign(&pkey, MessageDigest::sha256()).unwrap(); |
369 | let cert = builder.build(); |
370 | |
371 | let pkcs12 = Pkcs12::builder() |
372 | .name(subject_name) |
373 | .pkey(&pkey) |
374 | .cert(&cert) |
375 | .build2("mypass" ) |
376 | .unwrap(); |
377 | let der = pkcs12.to_der().unwrap(); |
378 | |
379 | let pkcs12 = Pkcs12::from_der(&der).unwrap(); |
380 | let parsed = pkcs12.parse2("mypass" ).unwrap(); |
381 | |
382 | assert_eq!( |
383 | &*parsed.cert.unwrap().digest(MessageDigest::sha1()).unwrap(), |
384 | &*cert.digest(MessageDigest::sha1()).unwrap() |
385 | ); |
386 | assert!(parsed.pkey.unwrap().public_eq(&pkey)); |
387 | } |
388 | |
389 | #[test ] |
390 | fn create_only_ca() { |
391 | let ca = include_bytes!("../test/root-ca.pem" ); |
392 | let ca = X509::from_pem(ca).unwrap(); |
393 | let mut chain = Stack::new().unwrap(); |
394 | chain.push(ca).unwrap(); |
395 | |
396 | let pkcs12 = Pkcs12::builder().ca(chain).build2("hunter2" ).unwrap(); |
397 | let parsed = pkcs12.parse2("hunter2" ).unwrap(); |
398 | |
399 | assert!(parsed.cert.is_none()); |
400 | assert!(parsed.pkey.is_none()); |
401 | assert_eq!(parsed.ca.unwrap().len(), 1); |
402 | } |
403 | } |
404 | |