| 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 | #[cfg (not(boringssl))] |
| 230 | let pass_len = pass.as_bytes().len(); |
| 231 | let pass = pass.as_ptr(); |
| 232 | let friendly_name = self.name.as_ref().map_or(ptr::null(), |p| p.as_ptr()); |
| 233 | let pkey = self.pkey.as_ref().map_or(ptr::null(), |p| p.as_ptr()); |
| 234 | let cert = self.cert.as_ref().map_or(ptr::null(), |p| p.as_ptr()); |
| 235 | let ca = self |
| 236 | .ca |
| 237 | .as_ref() |
| 238 | .map(|ca| ca.as_ptr()) |
| 239 | .unwrap_or(ptr::null_mut()); |
| 240 | let nid_key = self.nid_key.as_raw(); |
| 241 | let nid_cert = self.nid_cert.as_raw(); |
| 242 | |
| 243 | // According to the OpenSSL docs, keytype is a non-standard extension for MSIE, |
| 244 | // It's values are KEY_SIG or KEY_EX, see the OpenSSL docs for more information: |
| 245 | // https://www.openssl.org/docs/manmaster/crypto/PKCS12_create.html |
| 246 | let keytype = 0; |
| 247 | |
| 248 | let pkcs12 = cvt_p(ffi::PKCS12_create( |
| 249 | pass as *mut _, |
| 250 | friendly_name as *mut _, |
| 251 | pkey as *mut _, |
| 252 | cert as *mut _, |
| 253 | ca, |
| 254 | nid_key, |
| 255 | nid_cert, |
| 256 | self.iter, |
| 257 | self.mac_iter, |
| 258 | keytype, |
| 259 | )) |
| 260 | .map(Pkcs12)?; |
| 261 | |
| 262 | #[cfg (not(boringssl))] |
| 263 | // BoringSSL does not support overriding the MAC and will always |
| 264 | // use SHA-1. |
| 265 | { |
| 266 | let md_type = self |
| 267 | .mac_md |
| 268 | .map(|md_type| md_type.as_ptr()) |
| 269 | .unwrap_or(ptr::null()); |
| 270 | |
| 271 | cvt(ffi::PKCS12_set_mac( |
| 272 | pkcs12.as_ptr(), |
| 273 | pass, |
| 274 | pass_len.try_into().unwrap(), |
| 275 | ptr::null_mut(), |
| 276 | 0, |
| 277 | self.mac_iter, |
| 278 | md_type, |
| 279 | ))?; |
| 280 | } |
| 281 | |
| 282 | Ok(pkcs12) |
| 283 | } |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | #[cfg (test)] |
| 288 | mod test { |
| 289 | use crate::asn1::Asn1Time; |
| 290 | use crate::hash::MessageDigest; |
| 291 | use crate::nid::Nid; |
| 292 | use crate::pkey::PKey; |
| 293 | use crate::rsa::Rsa; |
| 294 | use crate::x509::extension::KeyUsage; |
| 295 | use crate::x509::{X509Name, X509}; |
| 296 | |
| 297 | use super::*; |
| 298 | |
| 299 | #[test ] |
| 300 | fn parse() { |
| 301 | #[cfg (ossl300)] |
| 302 | let _provider = crate::provider::Provider::try_load(None, "legacy" , true).unwrap(); |
| 303 | |
| 304 | let der = include_bytes!("../test/identity.p12" ); |
| 305 | let pkcs12 = Pkcs12::from_der(der).unwrap(); |
| 306 | let parsed = pkcs12.parse2("mypass" ).unwrap(); |
| 307 | |
| 308 | assert_eq!( |
| 309 | hex::encode( |
| 310 | parsed |
| 311 | .cert |
| 312 | .as_ref() |
| 313 | .unwrap() |
| 314 | .digest(MessageDigest::sha1()) |
| 315 | .unwrap() |
| 316 | ), |
| 317 | "59172d9313e84459bcff27f967e79e6e9217e584" |
| 318 | ); |
| 319 | assert_eq!( |
| 320 | parsed.cert.as_ref().unwrap().alias(), |
| 321 | Some(b"foobar.com" as &[u8]) |
| 322 | ); |
| 323 | |
| 324 | let chain = parsed.ca.unwrap(); |
| 325 | assert_eq!(chain.len(), 1); |
| 326 | assert_eq!( |
| 327 | hex::encode(chain[0].digest(MessageDigest::sha1()).unwrap()), |
| 328 | "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875" |
| 329 | ); |
| 330 | } |
| 331 | |
| 332 | #[test ] |
| 333 | fn parse_empty_chain() { |
| 334 | #[cfg (ossl300)] |
| 335 | let _provider = crate::provider::Provider::try_load(None, "legacy" , true).unwrap(); |
| 336 | |
| 337 | let der = include_bytes!("../test/keystore-empty-chain.p12" ); |
| 338 | let pkcs12 = Pkcs12::from_der(der).unwrap(); |
| 339 | let parsed = pkcs12.parse2("cassandra" ).unwrap(); |
| 340 | if let Some(stack) = parsed.ca { |
| 341 | assert_eq!(stack.len(), 0); |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | #[test ] |
| 346 | fn create() { |
| 347 | let subject_name = "ns.example.com" ; |
| 348 | let rsa = Rsa::generate(2048).unwrap(); |
| 349 | let pkey = PKey::from_rsa(rsa).unwrap(); |
| 350 | |
| 351 | let mut name = X509Name::builder().unwrap(); |
| 352 | name.append_entry_by_nid(Nid::COMMONNAME, subject_name) |
| 353 | .unwrap(); |
| 354 | let name = name.build(); |
| 355 | |
| 356 | let key_usage = KeyUsage::new().digital_signature().build().unwrap(); |
| 357 | |
| 358 | let mut builder = X509::builder().unwrap(); |
| 359 | builder.set_version(2).unwrap(); |
| 360 | builder |
| 361 | .set_not_before(&Asn1Time::days_from_now(0).unwrap()) |
| 362 | .unwrap(); |
| 363 | builder |
| 364 | .set_not_after(&Asn1Time::days_from_now(365).unwrap()) |
| 365 | .unwrap(); |
| 366 | builder.set_subject_name(&name).unwrap(); |
| 367 | builder.set_issuer_name(&name).unwrap(); |
| 368 | builder.append_extension(key_usage).unwrap(); |
| 369 | builder.set_pubkey(&pkey).unwrap(); |
| 370 | builder.sign(&pkey, MessageDigest::sha256()).unwrap(); |
| 371 | let cert = builder.build(); |
| 372 | |
| 373 | let pkcs12 = Pkcs12::builder() |
| 374 | .name(subject_name) |
| 375 | .pkey(&pkey) |
| 376 | .cert(&cert) |
| 377 | .build2("mypass" ) |
| 378 | .unwrap(); |
| 379 | let der = pkcs12.to_der().unwrap(); |
| 380 | |
| 381 | let pkcs12 = Pkcs12::from_der(&der).unwrap(); |
| 382 | let parsed = pkcs12.parse2("mypass" ).unwrap(); |
| 383 | |
| 384 | assert_eq!( |
| 385 | &*parsed.cert.unwrap().digest(MessageDigest::sha1()).unwrap(), |
| 386 | &*cert.digest(MessageDigest::sha1()).unwrap() |
| 387 | ); |
| 388 | assert!(parsed.pkey.unwrap().public_eq(&pkey)); |
| 389 | } |
| 390 | |
| 391 | #[test ] |
| 392 | fn create_only_ca() { |
| 393 | let ca = include_bytes!("../test/root-ca.pem" ); |
| 394 | let ca = X509::from_pem(ca).unwrap(); |
| 395 | let mut chain = Stack::new().unwrap(); |
| 396 | chain.push(ca).unwrap(); |
| 397 | |
| 398 | let pkcs12 = Pkcs12::builder().ca(chain).build2("hunter2" ).unwrap(); |
| 399 | let parsed = pkcs12.parse2("hunter2" ).unwrap(); |
| 400 | |
| 401 | assert!(parsed.cert.is_none()); |
| 402 | assert!(parsed.pkey.is_none()); |
| 403 | assert_eq!(parsed.ca.unwrap().len(), 1); |
| 404 | } |
| 405 | } |
| 406 | |