1use std::{fmt, str::FromStr};
39
40use base64::Engine;
41
42use crate::x509::{BASE64, Certificate, Error, PrivateKey, crypto};
43
44#[cfg(windows)]
45pub use crate::x509::cng::CngKey;
46pub use crate::x509::key::{HashAlg, SignScheme};
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum StoreLocation {
51 CurrentUser,
53 LocalMachine,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59pub enum StoreSelector {
60 Thumbprint([u8; 20]),
62 Subject(String),
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
68pub enum IdentitySource {
69 Pkcs12File(String),
71 PemFiles {
73 certificates: String,
75 private_key: String,
77 },
78 Store {
80 location: StoreLocation,
81 store: String,
82 select: StoreSelector,
83 },
84}
85
86impl FromStr for IdentitySource {
87 type Err = Error;
88
89 fn from_str(s: &str) -> Result<Self, Error> {
101 let Some(rest) = s
102 .get(..5)
103 .filter(|p| p.eq_ignore_ascii_case("cert:"))
104 .map(|_| &s[5..])
105 else {
106 return Ok(IdentitySource::Pkcs12File(s.to_string()));
107 };
108 if cfg!(not(windows)) {
109 return Err(Error::Unsupported(format!(
110 "certificate store identity is Windows only: {s}"
111 )));
112 }
113 let invalid = || {
114 Error::InvalidIdentity(format!(
115 "expected cert:<Location>\\<Store>\\<thumbprint|subject=..>: {s}"
116 ))
117 };
118
119 let mut parts = rest.trim_start_matches(['\\', '/']).splitn(3, ['\\', '/']);
120 let (Some(location), Some(store), Some(select)) =
121 (parts.next(), parts.next(), parts.next())
122 else {
123 return Err(invalid());
124 };
125 let location = if location.eq_ignore_ascii_case("CurrentUser") {
126 StoreLocation::CurrentUser
127 } else if location.eq_ignore_ascii_case("LocalMachine") {
128 StoreLocation::LocalMachine
129 } else {
130 return Err(invalid());
131 };
132 if store.is_empty() {
133 return Err(invalid());
134 }
135 let select = if let Some(subject) = select.strip_prefix("subject=") {
136 if subject.is_empty() {
137 return Err(invalid());
138 }
139 StoreSelector::Subject(subject.to_string())
140 } else {
141 let mut tp = [0u8; 20];
142 hex::decode_to_slice(select, &mut tp).map_err(|_| invalid())?;
143 StoreSelector::Thumbprint(tp)
144 };
145 Ok(IdentitySource::Store {
146 location,
147 store: store.to_string(),
148 select,
149 })
150 }
151}
152
153impl IdentitySource {
154 pub fn load(&self) -> Result<Identity, Error> {
160 match self {
161 IdentitySource::Pkcs12File(path) => {
162 let der =
163 std::fs::read(path).map_err(|e| Error::NotFound(format!("{path}: {e}")))?;
164 Identity::from_pkcs12(&der, "")
165 }
166 IdentitySource::PemFiles {
167 certificates,
168 private_key,
169 } => {
170 let certs = std::fs::read(certificates)
171 .map_err(|e| Error::NotFound(format!("{certificates}: {e}")))?;
172 let key = zeroize::Zeroizing::new(
173 std::fs::read(private_key)
174 .map_err(|e| Error::NotFound(format!("{private_key}: {e}")))?,
175 );
176 Identity::from_pem(&certs, &key)
177 }
178 IdentitySource::Store {
179 location,
180 store,
181 select,
182 } => Identity::from_store(*location, store, select),
183 }
184 }
185}
186
187#[derive(Clone)]
189pub struct Identity {
190 cert: Certificate,
191 chain: Vec<Certificate>,
192 key: PrivateKey,
193 #[cfg(windows)]
194 schannel: crate::schannel::Certificate,
195}
196
197impl Identity {
198 pub fn from_der(certificates: &[&[u8]], private_key: &[u8]) -> Result<Self, Error> {
204 let certs = certificates
205 .iter()
206 .map(|der| Certificate::from_der(ntex_bytes::Bytes::copy_from_slice(der)))
207 .collect::<Result<_, _>>()?;
208 Self::from_certificates(certs, private_key)
209 }
210
211 pub fn from_pem(certificates: &[u8], private_key: &[u8]) -> Result<Self, Error> {
218 let key = pkcs8_pem(private_key)?;
219 Self::from_certificates(Certificate::from_pem_multi(certificates)?, &key)
220 }
221
222 fn from_certificates(mut certs: Vec<Certificate>, key: &[u8]) -> Result<Self, Error> {
223 if certs.is_empty() {
224 return Err(Error::InvalidIdentity("Certificate is missing".into()));
225 }
226 let private_key = PrivateKey::from_pkcs8(&certs[0], key)?;
227 #[cfg(not(windows))]
228 {
229 let cert = certs.remove(0);
230 Ok(Self {
231 cert,
232 chain: certs,
233 key: private_key,
234 })
235 }
236 #[cfg(windows)]
237 {
238 drop(private_key);
239 let schannel = crate::schannel::Certificate::from_pkcs8(&certs, key)
240 .map_err(|e| Error::InvalidIdentity(e.to_string()))?;
241 let mut identity = Self::from_schannel(schannel)?;
243 certs.remove(0);
244 identity.chain = certs;
245 Ok(identity)
246 }
247 }
248
249 pub fn from_pkcs12(der: &[u8], password: &str) -> Result<Self, Error> {
258 #[cfg(all(unix, feature = "openssl"))]
259 {
260 let invalid = |e: tls_openssl::error::ErrorStack| Error::InvalidIdentity(e.to_string());
261 let parsed = tls_openssl::pkcs12::Pkcs12::from_der(der)
262 .map_err(|e| Error::InvalidDer(e.to_string()))?
263 .parse2(password)
264 .map_err(invalid)?;
265 let cert = parsed
266 .cert
267 .ok_or_else(|| Error::InvalidIdentity("Certificate is missing".into()))?;
268 let key = parsed
269 .pkey
270 .ok_or_else(|| Error::InvalidIdentity("Private key is missing".into()))?;
271 let key = zeroize::Zeroizing::new(key.private_key_to_pkcs8().map_err(invalid)?);
272 let mut chain = vec![Certificate::from_der(cert.to_der().map_err(invalid)?)?];
273 chain.extend(
274 parsed
275 .ca
276 .into_iter()
277 .flatten()
278 .map(|c| Certificate::from_der(c.to_der().map_err(invalid)?))
279 .collect::<Result<Vec<_>, _>>()?,
280 );
281 Self::from_certificates(chain, &key)
282 }
283 #[cfg(windows)]
284 {
285 crate::x509::cng::check_pfx(der)?;
286 let schannel = crate::schannel::Certificate::from_pkcs12(der, password)
287 .map_err(|e| Error::InvalidIdentity(e.to_string()))?;
288 Self::from_schannel(schannel)
289 }
290 #[cfg(not(any(windows, all(unix, feature = "openssl"))))]
291 {
292 let _ = (der, password);
293 Err(Error::Unsupported(
294 "PKCS#12 loading requires the openssl feature on Unix".into(),
295 ))
296 }
297 }
298
299 pub fn from_store(
306 location: StoreLocation,
307 store: &str,
308 select: &StoreSelector,
309 ) -> Result<Self, Error> {
310 #[cfg(not(windows))]
311 {
312 let _ = (location, store, select);
313 Err(Error::Unsupported(
314 "certificate stores require Windows".into(),
315 ))
316 }
317 #[cfg(windows)]
318 {
319 use crate::schannel::{CertStoreLocation, Certificate as SchannelCert};
320
321 let location = match location {
322 StoreLocation::CurrentUser => CertStoreLocation::CurrentUser,
323 StoreLocation::LocalMachine => CertStoreLocation::LocalMachine,
324 };
325 let result = match select {
326 StoreSelector::Thumbprint(tp) => SchannelCert::from_store(location, store, tp),
327 StoreSelector::Subject(subject) => {
328 SchannelCert::from_store_by_subject(location, store, subject)
329 }
330 };
331 let schannel = result.map_err(|e| match e.kind() {
332 std::io::ErrorKind::NotFound => Error::NotFound(e.to_string()),
333 _ => Error::InvalidIdentity(e.to_string()),
334 })?;
335 Self::from_schannel(schannel)
336 }
337 }
338
339 #[cfg(windows)]
340 fn from_schannel(schannel: crate::schannel::Certificate) -> Result<Self, Error> {
341 let (mut chain, key) = crate::x509::cng::from_schannel(schannel.clone())?;
342 let cert = chain.remove(0);
343 let key = PrivateKey::from_native(cert.clone(), key);
344 Ok(Identity {
345 cert,
346 chain,
347 key,
348 schannel,
349 })
350 }
351
352 #[must_use]
354 pub fn cert(&self) -> &Certificate {
355 &self.cert
356 }
357
358 #[must_use]
362 pub fn chain(&self) -> &[Certificate] {
363 &self.chain
364 }
365
366 pub fn ordered_chain(&self, include_root: bool) -> Result<Vec<Certificate>, Error> {
377 order_chain(&self.cert, self.chain.clone(), include_root)
378 }
379
380 #[must_use]
382 pub fn private_key(&self) -> &PrivateKey {
383 &self.key
384 }
385
386 #[cfg(not(windows))]
388 #[must_use]
389 pub fn private_key_pkcs8_der(&self) -> &[u8] {
390 &self.key.der
391 }
392
393 #[cfg(windows)]
395 #[must_use]
396 pub fn schannel(&self) -> &crate::schannel::Certificate {
397 &self.schannel
398 }
399}
400
401impl fmt::Debug for Identity {
402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403 f.debug_struct("Identity")
404 .field("cert", &self.cert)
405 .field("chain", &self.chain())
406 .field("key", &"<redacted>")
407 .finish()
408 }
409}
410
411#[derive(Clone)]
413pub struct ClientIdentity {
414 chain: Vec<Certificate>,
415 key: PrivateKey,
416}
417
418impl ClientIdentity {
419 pub fn from_der(certificates: &[&[u8]], private_key: &[u8]) -> Result<Self, Error> {
424 let certs = certificates
425 .iter()
426 .map(|der| Certificate::from_der(ntex_bytes::Bytes::copy_from_slice(der)))
427 .collect::<Result<_, _>>()?;
428 Self::from_certificates(certs, private_key)
429 }
430
431 pub fn from_pem(certificates: &[u8], private_key: &[u8]) -> Result<Self, Error> {
436 let key = pkcs8_pem(private_key)?;
437 Self::from_certificates(Certificate::from_pem_multi(certificates)?, &key)
438 }
439
440 fn from_certificates(mut certs: Vec<Certificate>, private_key: &[u8]) -> Result<Self, Error> {
441 if certs.is_empty() {
442 return Err(Error::InvalidIdentity("Certificate is missing".into()));
443 }
444 let cert = certs.remove(0);
445 let key = PrivateKey::from_pkcs8(&cert, private_key)?;
446 let mut chain = vec![cert.clone()];
447 chain.extend(order_chain(&cert, certs, false)?);
448 Ok(Self { chain, key })
449 }
450
451 pub fn load(source: &IdentitySource) -> Result<Self, Error> {
458 #[cfg(not(windows))]
459 {
460 Self::from_identity(source.load()?)
461 }
462 #[cfg(windows)]
463 {
464 let (chain, key) = match source {
465 IdentitySource::Pkcs12File(path) => {
466 let der =
467 std::fs::read(path).map_err(|e| Error::NotFound(format!("{path}: {e}")))?;
468 return Self::from_pkcs12(&der, "");
469 }
470 IdentitySource::PemFiles {
471 certificates,
472 private_key,
473 } => {
474 let certs = std::fs::read(certificates)
475 .map_err(|e| Error::NotFound(format!("{certificates}: {e}")))?;
476 let key = zeroize::Zeroizing::new(
477 std::fs::read(private_key)
478 .map_err(|e| Error::NotFound(format!("{private_key}: {e}")))?,
479 );
480 return Self::from_pem(&certs, &key);
481 }
482 IdentitySource::Store {
483 location,
484 store,
485 select,
486 } => crate::x509::cng::from_store(*location, store, select)?,
487 };
488 let key = PrivateKey::from_native(chain[0].clone(), key);
489 Ok(ClientIdentity { chain, key })
490 }
491 }
492
493 pub fn from_pkcs12(der: &[u8], password: &str) -> Result<Self, Error> {
501 #[cfg(not(windows))]
502 {
503 Identity::from_pkcs12(der, password).and_then(Self::from_identity)
504 }
505 #[cfg(windows)]
506 {
507 let (chain, key) = crate::x509::cng::from_pkcs12(der, password)?;
508 let key = PrivateKey::from_native(chain[0].clone(), key);
509 Ok(ClientIdentity { chain, key })
510 }
511 }
512
513 pub fn from_identity(id: Identity) -> Result<Self, Error> {
518 let mut chain = vec![id.cert.clone()];
519 chain.extend(order_chain(&id.cert, id.chain, false)?);
520 Ok(ClientIdentity { chain, key: id.key })
521 }
522
523 #[must_use]
525 pub fn cert(&self) -> &Certificate {
526 &self.chain[0]
527 }
528
529 #[must_use]
531 pub fn chain(&self) -> &[Certificate] {
532 &self.chain
533 }
534
535 #[must_use]
537 pub fn private_key(&self) -> &PrivateKey {
538 &self.key
539 }
540
541 #[cfg(not(windows))]
543 #[must_use]
544 pub fn private_key_pkcs8_der(&self) -> &[u8] {
545 &self.key.der
546 }
547
548 #[cfg(windows)]
550 #[must_use]
551 pub fn key(&self) -> &CngKey {
552 &self.key.native
553 }
554}
555
556impl fmt::Debug for ClientIdentity {
557 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
558 f.debug_struct("ClientIdentity")
559 .field("chain", &self.chain)
560 .field("key", &"<redacted>")
561 .finish()
562 }
563}
564
565fn order_chain(
571 leaf: &Certificate,
572 extras: Vec<Certificate>,
573 include_root: bool,
574) -> Result<Vec<Certificate>, Error> {
575 let mut current = leaf.clone();
576 if crypto::is_self_signed(¤t)? {
577 if !extras.is_empty() {
578 log::warn!("TLS certificate is self-signed, but certificate chain was provided");
579 }
580 return Ok(Vec::new());
581 }
582 let mut extras = extras;
583 let mut chain = Vec::new();
584 loop {
585 let mut issuer = None;
586 for (pos, candidate) in extras.iter().enumerate() {
587 if crypto::is_issuer(¤t, candidate)? {
588 issuer = Some(pos);
589 break;
590 }
591 }
592 let Some(pos) = issuer else { break };
593 let cert = extras.swap_remove(pos);
594 if crypto::is_self_signed(&cert)? {
595 if include_root {
596 chain.push(cert);
597 }
598 break;
599 }
600 chain.push(cert.clone());
601 current = cert;
602 }
603 if !extras.is_empty() {
604 log::warn!("TLS certificate chain contains extra certificates");
605 }
606 Ok(chain)
607}
608
609pub(crate) fn pkcs8_pem(pem: &[u8]) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
611 let text = std::str::from_utf8(pem).map_err(|e| Error::InvalidPem(e.to_string()))?;
612 for block in text.split("-----BEGIN ").skip(1) {
613 if let Some((label, _)) = block.split_once("-----")
614 && label.ends_with("PRIVATE KEY")
615 && label != "PRIVATE KEY"
616 {
617 return Err(Error::InvalidPem(
618 "only unencrypted PKCS#8 PRIVATE KEY blocks are supported".into(),
619 ));
620 }
621 }
622 let begin = "-----BEGIN PRIVATE KEY-----";
623 let end = "-----END PRIVATE KEY-----";
624 let mut starts = text.match_indices(begin);
625 let (start, _) = starts.next().ok_or_else(|| {
626 Error::InvalidPem("one unencrypted PKCS#8 PRIVATE KEY is required".into())
627 })?;
628 if starts.next().is_some() {
629 return Err(Error::InvalidPem("multiple PRIVATE KEY blocks".into()));
630 }
631 let body = &text[start + begin.len()..];
632 let end = body
633 .find(end)
634 .ok_or_else(|| Error::InvalidPem("missing PRIVATE KEY END".into()))?;
635 let encoded = zeroize::Zeroizing::new(
636 body[..end]
637 .bytes()
638 .filter(|b| !b.is_ascii_whitespace())
639 .collect::<Vec<_>>(),
640 );
641 BASE64
642 .decode(encoded.as_slice())
643 .map(zeroize::Zeroizing::new)
644 .map_err(|e| Error::InvalidPem(e.to_string()))
645}
646
647#[cfg(any(windows, all(test, unix, feature = "openssl")))]
650pub(crate) fn ecdsa_p1363_to_der(raw: &[u8]) -> Vec<u8> {
651 fn push_len(out: &mut Vec<u8>, len: usize) {
652 if len >= 0x80 {
654 out.push(0x81);
655 }
656 out.push(u8::try_from(len).unwrap_or(u8::MAX));
657 }
658
659 let (r, s) = raw.split_at(raw.len() / 2);
660 let mut body = Vec::with_capacity(raw.len() + 8);
661 for int in [r, s] {
662 let int = int
663 .iter()
664 .position(|b| *b != 0)
665 .map_or(&[0u8][..], |i| &int[i..]);
666 let pad = int[0] & 0x80 != 0;
667 body.push(0x02);
668 push_len(&mut body, int.len() + usize::from(pad));
669 if pad {
670 body.push(0);
671 }
672 body.extend_from_slice(int);
673 }
674 let mut out = Vec::with_capacity(body.len() + 3);
675 out.push(0x30);
676 push_len(&mut out, body.len());
677 out.extend_from_slice(&body);
678 out
679}
680
681#[cfg(test)]
682mod tests {
683 use super::*;
684 use crate::x509::testgen::{self, San};
685
686 #[test]
693 fn pem_der_identities() {
694 crate::x509::test_provider();
695 let pem = include_bytes!("../../examples/cert.pem");
696 let key_pem = include_bytes!("../../examples/key.pem");
697 let key_der = pkcs8_pem(key_pem).unwrap();
698 let identity = Identity::from_pem(pem, key_pem).unwrap();
699 let der_identity = Identity::from_der(&[identity.cert().der()], &key_der).unwrap();
700 assert_eq!(identity.cert(), der_identity.cert());
701 let client = ClientIdentity::from_pem(pem, key_pem).unwrap();
702 let der_client = ClientIdentity::from_der(&[identity.cert().der()], &key_der).unwrap();
703 assert_eq!(client.chain(), der_client.chain());
704 assert_eq!(client.cert(), identity.cert());
705 assert_eq!(
706 client.private_key().public_key_der(),
707 identity.cert().public_key_der()
708 );
709 assert!(format!("{:?}", client.private_key()).contains("<redacted>"));
710 let key = client.private_key().clone();
711 drop(client);
712 let message = b"portable identity signing";
713 let signature = key
714 .sign(SignScheme::RsaPss(HashAlg::Sha256), message)
715 .unwrap();
716 assert!(matches!(
717 key.sign(SignScheme::Ecdsa(HashAlg::Sha256), message),
718 Err(Error::Unsupported(_))
719 ));
720 #[cfg(not(windows))]
721 assert_eq!(key.export_pkcs8_der().unwrap(), &*key_der);
722 #[cfg(windows)]
723 assert!(matches!(key.export_pkcs8_der(), Err(Error::Unsupported(_))));
724 #[cfg(feature = "rustls")]
725 {
726 use tls_rustls::pki_types::CertificateDer;
727 let certified = crate::rustls::certified_key(&identity).unwrap();
728 certified.keys_match().unwrap();
729 let cert_der = CertificateDer::from(identity.cert().der().to_vec());
730 let cert = webpki::EndEntityCert::try_from(&cert_der).unwrap();
731 let provider = tls_rustls::crypto::ring::default_provider();
732 let algorithms = provider
733 .signature_verification_algorithms
734 .mapping
735 .iter()
736 .find(|(scheme, _)| *scheme == tls_rustls::SignatureScheme::RSA_PSS_SHA256)
737 .unwrap()
738 .1;
739 assert!(
740 algorithms
741 .iter()
742 .any(|alg| cert.verify_signature(*alg, message, &signature).is_ok())
743 );
744 let signer = certified
745 .key
746 .choose_scheme(&[tls_rustls::SignatureScheme::RSA_PSS_SHA256])
747 .unwrap();
748 drop(certified);
749 drop(identity);
750 let signature = signer.sign(message).unwrap();
751 assert!(
752 algorithms
753 .iter()
754 .any(|alg| cert.verify_signature(*alg, message, &signature).is_ok())
755 );
756 }
757 #[cfg(all(not(windows), not(feature = "rustls"), feature = "openssl"))]
758 {
759 use tls_openssl::{
760 hash::MessageDigest,
761 rsa::Padding,
762 sign::{RsaPssSaltlen, Verifier},
763 ssl,
764 };
765 let mut context = ssl::SslContextBuilder::new(ssl::SslMethod::tls()).unwrap();
766 crate::openssl::set_identity(&mut context, &identity).unwrap();
767 let public =
768 tls_openssl::pkey::PKey::public_key_from_der(key.public_key_der()).unwrap();
769 let mut verifier = Verifier::new(MessageDigest::sha256(), &public).unwrap();
770 verifier.set_rsa_padding(Padding::PKCS1_PSS).unwrap();
771 verifier.set_rsa_mgf1_md(MessageDigest::sha256()).unwrap();
772 verifier
773 .set_rsa_pss_saltlen(RsaPssSaltlen::DIGEST_LENGTH)
774 .unwrap();
775 assert!(verifier.verify_oneshot(&signature, message).unwrap());
776 }
777 #[cfg(all(windows, not(feature = "rustls")))]
778 assert!(!signature.is_empty());
779 }
780
781 #[test]
788 fn invalid_pem_der_identities() {
789 crate::x509::test_provider();
790 let pem = include_bytes!("../../examples/cert.pem");
791 let key = include_bytes!("../../examples/key.pem");
792 let wrong = include_bytes!("../../tests/testdata/leaf.pem");
793 assert!(matches!(
794 Identity::from_pem(wrong, key),
795 Err(Error::InvalidIdentity(_))
796 ));
797 assert!(matches!(
798 ClientIdentity::from_pem(wrong, key),
799 Err(Error::InvalidIdentity(_))
800 ));
801 assert!(Identity::from_der(&[], &pkcs8_pem(key).unwrap()).is_err());
802 assert!(ClientIdentity::from_der(&[], &pkcs8_pem(key).unwrap()).is_err());
803 let cert = Certificate::from_pem(pem).unwrap();
804 assert!(Identity::from_der(&[cert.der()], b"invalid").is_err());
805 assert!(ClientIdentity::from_der(&[b"invalid"], &pkcs8_pem(key).unwrap()).is_err());
806 assert!(Identity::from_pem(pem, b"").is_err());
807 let repeated = [key.as_slice(), key.as_slice()].concat();
808 assert!(matches!(
809 Identity::from_pem(pem, &repeated),
810 Err(Error::InvalidPem(_))
811 ));
812 let mixed = [key.as_slice(), b"-----BEGIN RSA PRIVATE KEY-----"].concat();
813 assert!(matches!(
814 Identity::from_pem(pem, &mixed),
815 Err(Error::InvalidPem(_))
816 ));
817 for invalid in [
818 b"\xff".as_slice(),
819 b"-----BEGIN PRIVATE KEY-----\nAAAA",
820 b"-----BEGIN PRIVATE KEY-----\n!\n-----END PRIVATE KEY-----",
821 b"-----BEGIN ENCRYPTED PRIVATE KEY-----\nAAAA\n-----END ENCRYPTED PRIVATE KEY-----",
822 b"-----BEGIN EC PRIVATE KEY-----\nAAAA\n-----END EC PRIVATE KEY-----",
823 ] {
824 assert!(matches!(
825 Identity::from_pem(pem, invalid),
826 Err(Error::InvalidPem(_))
827 ));
828 assert!(matches!(
829 ClientIdentity::from_pem(pem, invalid),
830 Err(Error::InvalidPem(_))
831 ));
832 }
833 }
834
835 #[test]
841 fn pem_source_missing_files() {
842 let certificate = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/cert.pem");
843 let missing = format!("{certificate}/missing");
844 for (certificates, private_key) in [
845 (missing.as_str(), certificate),
846 (certificate, missing.as_str()),
847 ] {
848 let source = IdentitySource::PemFiles {
849 certificates: certificates.into(),
850 private_key: private_key.into(),
851 };
852 for result in [
853 source.load().map(|_| ()),
854 ClientIdentity::load(&source).map(|_| ()),
855 ] {
856 assert!(
857 matches!(result, Err(Error::NotFound(message)) if message.contains(&missing))
858 );
859 }
860 }
861 }
862
863 #[test]
869 fn pem_sources() {
870 crate::x509::test_provider();
871 let source = IdentitySource::PemFiles {
872 certificates: concat!(env!("CARGO_MANIFEST_DIR"), "/examples/cert.pem").into(),
873 private_key: concat!(env!("CARGO_MANIFEST_DIR"), "/examples/key.pem").into(),
874 };
875 let identity = source.load().unwrap();
876 let client = ClientIdentity::load(&source).unwrap();
877 let combined = zeroize::Zeroizing::new(
878 [
879 include_bytes!("../../examples/cert.pem").as_slice(),
880 include_bytes!("../../examples/key.pem").as_slice(),
881 ]
882 .concat(),
883 );
884 assert_eq!(
885 Identity::from_pem(&combined, &combined).unwrap().cert(),
886 identity.cert()
887 );
888 assert_eq!(
889 ClientIdentity::from_pem(&combined, &combined)
890 .unwrap()
891 .cert(),
892 client.cert()
893 );
894 #[cfg(feature = "rustls")]
895 {
896 let certified = crate::rustls::client_certified_key(&client);
897 certified.keys_match().unwrap();
898 assert_eq!(certified.cert[0].as_ref(), identity.cert().der().as_ref());
899 }
900 }
901
902 #[test]
908 fn portable_chain_ordering() {
909 crate::x509::test_provider();
910 let leaf = Certificate::from_pem(include_bytes!("../../tests/testdata/leaf.pem")).unwrap();
911 let inter =
912 Certificate::from_pem(include_bytes!("../../tests/testdata/inter.pem")).unwrap();
913 let root = Certificate::from_pem(include_bytes!("../../tests/testdata/root.pem")).unwrap();
914 let other =
915 Certificate::from_pem(include_bytes!("../../tests/testdata/other.pem")).unwrap();
916 let extras = vec![other, root.clone(), inter.clone()];
917 assert_eq!(
918 order_chain(&leaf, extras.clone(), false).unwrap(),
919 vec![inter.clone()]
920 );
921 assert_eq!(
922 order_chain(&leaf, extras, true).unwrap(),
923 vec![inter.clone(), root.clone()]
924 );
925 assert_eq!(
926 order_chain(&leaf, vec![root.clone()], true).unwrap(),
927 Vec::<Certificate>::new()
928 );
929 let mut der = inter.der().to_vec();
930 *der.last_mut().unwrap() ^= 1;
931 let corrupt = Certificate::from_der(der).unwrap();
932 assert_eq!(
933 order_chain(&corrupt, vec![root], true).unwrap(),
934 Vec::<Certificate>::new()
935 );
936 }
937
938 #[cfg(all(not(windows), not(feature = "openssl")))]
944 #[test]
945 fn pkcs12_requires_openssl() {
946 assert!(matches!(
947 Identity::from_pkcs12(b"archive", ""),
948 Err(Error::Unsupported(_))
949 ));
950 assert!(matches!(
951 ClientIdentity::from_pkcs12(b"archive", ""),
952 Err(Error::Unsupported(_))
953 ));
954 }
955
956 #[test]
962 fn source_from_str() {
963 assert_eq!(
964 "/etc/cert.pfx".parse::<IdentitySource>().unwrap(),
965 IdentitySource::Pkcs12File("/etc/cert.pfx".into())
966 );
967 let store = "CERT:LocalMachine\\MY\\0102030405060708090a0b0c0d0e0f1011121314"
968 .parse::<IdentitySource>();
969 let subject = "cert:/CurrentUser/My/subject=gw.example.com".parse::<IdentitySource>();
970 if cfg!(windows) {
971 assert_eq!(
972 store.unwrap(),
973 IdentitySource::Store {
974 location: StoreLocation::LocalMachine,
975 store: "MY".into(),
976 select: StoreSelector::Thumbprint([
977 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
978 ]),
979 }
980 );
981 assert_eq!(
982 subject.unwrap(),
983 IdentitySource::Store {
984 location: StoreLocation::CurrentUser,
985 store: "My".into(),
986 select: StoreSelector::Subject("gw.example.com".into()),
987 }
988 );
989 for bad in [
990 "cert:Other\\MY\\00",
991 "cert:LocalMachine\\MY",
992 "cert:LocalMachine\\MY\\abc",
993 "cert:LocalMachine\\MY\\subject=",
994 ] {
995 assert!(
996 matches!(
997 bad.parse::<IdentitySource>(),
998 Err(Error::InvalidIdentity(_))
999 ),
1000 "{bad}"
1001 );
1002 }
1003 } else {
1004 assert!(matches!(store, Err(Error::Unsupported(_))));
1005 assert!(matches!(subject, Err(Error::Unsupported(_))));
1006 }
1007 }
1008
1009 #[cfg(any(windows, all(unix, feature = "openssl")))]
1017 #[test]
1018 fn pkcs12_roundtrip() {
1019 let san = San::default();
1020 let rk = testgen::key();
1021 let root = testgen::cert("root", true, None, &rk, &san, (-1, 10));
1022 let lk = testgen::key();
1023 let leaf = testgen::cert("leaf", false, Some((&root, &rk)), &lk, &san, (-1, 10));
1024 let pfx = testgen::pkcs12(&leaf, &lk, &[&root], "pw");
1025
1026 let id = Identity::from_pkcs12(&pfx, "pw").unwrap();
1027 assert_eq!(id.cert(), &leaf);
1028 if cfg!(windows) {
1029 assert!(id.chain().len() <= 1);
1030 assert!(id.chain().iter().all(|c| c == &root));
1031 } else {
1032 assert_eq!(id.chain(), [root]);
1033 }
1034 assert_eq!(id.private_key().public_key_der(), leaf.public_key_der());
1035 #[cfg(not(windows))]
1036 assert!(!format!("{id:?}").contains(&hex::encode(id.private_key_pkcs8_der())));
1037 assert!(format!("{id:?}").contains("<redacted>"));
1038
1039 assert!(matches!(
1040 Identity::from_pkcs12(&pfx, "wrong"),
1041 Err(Error::InvalidIdentity(_))
1042 ));
1043 assert!(matches!(
1044 Identity::from_pkcs12(b"garbage", ""),
1045 Err(Error::InvalidDer(_))
1046 ));
1047 assert!(matches!(
1048 ClientIdentity::from_pkcs12(b"garbage", ""),
1049 Err(Error::InvalidDer(_))
1050 ));
1051 assert!(matches!(
1052 IdentitySource::Pkcs12File("/nonexistent/x.pfx".into()).load(),
1053 Err(Error::NotFound(_))
1054 ));
1055 }
1056
1057 #[test]
1066 fn client_identity_chain() {
1067 let san = San::default();
1068 let rk = testgen::key();
1069 let root = testgen::cert("root", true, None, &rk, &san, (-1, 10));
1070 let ik = testgen::key();
1071 let inter = testgen::cert("inter", true, Some((&root, &rk)), &ik, &san, (-1, 10));
1072 let lk = testgen::key();
1073 let leaf = testgen::cert("leaf", false, Some((&inter, &ik)), &lk, &san, (-1, 10));
1074 let other = testgen::cert("other", true, None, &testgen::key(), &san, (-1, 10));
1075
1076 let id = testgen::client(&leaf, &lk, &[&root, &other, &inter]);
1078 assert_eq!(id.chain(), [leaf.clone(), inter.clone()]);
1079 assert_eq!(id.cert(), &leaf);
1080 assert_eq!(id.cert().key_type(), Some(crate::x509::KeyType::EcP256));
1081 assert!(format!("{id:?}").contains("<redacted>"));
1082
1083 let certs = [&leaf, &root, &other, &inter].map(|c| c.der().as_ref());
1084 let id = Identity::from_der(&certs, &lk.pkcs8_der()).unwrap();
1085 assert_eq!(
1086 id.ordered_chain(false).unwrap(),
1087 std::slice::from_ref(&inter)
1088 );
1089 assert_eq!(
1090 id.ordered_chain(true).unwrap(),
1091 [inter.clone(), root.clone()]
1092 );
1093
1094 #[cfg(all(unix, feature = "openssl"))]
1095 {
1096 let pfx = testgen::pkcs12(&leaf, &lk, &[&root, &other, &inter], "");
1097 let path = std::env::temp_dir()
1098 .join(format!("ntex-tls-x509-client-{}.pfx", std::process::id()));
1099 std::fs::write(&path, &pfx).unwrap();
1100 let src = IdentitySource::Pkcs12File(path.to_string_lossy().into());
1101 let id = ClientIdentity::load(&src).unwrap();
1102 std::fs::remove_file(&path).unwrap();
1103 assert_eq!(id.chain(), [leaf.clone(), inter.clone()]);
1104 assert_eq!(id.private_key_pkcs8_der(), lk.pkcs8_der());
1105
1106 let id = Identity::from_pkcs12(&pfx, "").unwrap();
1107 assert_eq!(
1108 id.ordered_chain(true).unwrap(),
1109 [inter.clone(), root.clone()]
1110 );
1111 let mut context =
1112 tls_openssl::ssl::SslContextBuilder::new(tls_openssl::ssl::SslMethod::tls())
1113 .unwrap();
1114 crate::openssl::set_identity(&mut context, &id).unwrap();
1115 let context = context.build();
1116 let der = |c: &tls_openssl::x509::X509Ref| c.to_der().unwrap();
1117 assert_eq!(der(context.certificate().unwrap()), leaf.der().as_ref());
1118 assert_eq!(context.extra_chain_certs().len(), 1);
1119 assert_eq!(der(&context.extra_chain_certs()[0]), inter.der().as_ref());
1120 }
1121
1122 assert_eq!(
1124 order_chain(&root, vec![inter.clone()], true).unwrap(),
1125 Vec::<Certificate>::new()
1126 );
1127 let leaf2 = testgen::cert(
1129 "leaf2",
1130 false,
1131 Some((&inter, &ik)),
1132 &testgen::key(),
1133 &san,
1134 (-1, 10),
1135 );
1136 assert_eq!(
1137 order_chain(&leaf2, vec![root.clone()], true).unwrap(),
1138 Vec::<Certificate>::new()
1139 );
1140 }
1141
1142 #[cfg(all(unix, feature = "openssl", not(feature = "rustls")))]
1148 #[test]
1149 fn openssl_canonical_issuer() {
1150 let san = San::default();
1151 let key = testgen::key();
1152 let root = testgen::cert("Root CA", true, None, &key, &san, (-1, 10));
1153 let alias = testgen::cert(" root ca ", true, None, &key, &san, (-1, 10));
1154 let leaf = testgen::cert(
1155 "leaf",
1156 false,
1157 Some((&root, &key)),
1158 &testgen::key(),
1159 &san,
1160 (-1, 10),
1161 );
1162 assert_eq!(
1163 order_chain(&leaf, vec![alias.clone()], true).unwrap(),
1164 vec![alias]
1165 );
1166 }
1167
1168 #[test]
1176 fn client_identity_rollover() {
1177 let san = San::default();
1178 for rk in [testgen::key(), testgen::rsa_key()] {
1179 let root = testgen::cert("root", true, None, &rk, &san, (-1, 10));
1180 let ik = testgen::key();
1181 let inter = testgen::cert("root", true, Some((&root, &rk)), &ik, &san, (-1, 10));
1182 let lk = testgen::key();
1183 let leaf = testgen::cert("leaf", false, Some((&inter, &ik)), &lk, &san, (-1, 10));
1184 for extras in [&[&root, &inter][..], &[&inter][..]] {
1185 let id = testgen::client(&leaf, &lk, extras);
1186 assert_eq!(id.chain(), [leaf.clone(), inter.clone()]);
1187 }
1188
1189 let next_key = testgen::key();
1191 let next = testgen::cert("root", true, Some((&inter, &ik)), &next_key, &san, (-1, 10));
1192 let id = testgen::client(&next, &next_key, &[&root, &inter]);
1193 assert_eq!(id.chain(), [next, inter]);
1194 }
1195 }
1196
1197 #[test]
1204 fn client_identity_same_subject_issuers() {
1205 let san = San::default();
1206 let rk = testgen::key();
1207 let root = testgen::cert("root", true, None, &rk, &san, (-1, 10));
1208 let ik = testgen::key();
1209 let inter = testgen::cert("inter", true, Some((&root, &rk)), &ik, &san, (-1, 10));
1210 let lk = testgen::key();
1211 let leaf = testgen::cert("leaf", false, Some((&inter, &ik)), &lk, &san, (-1, 10));
1212 for key in [testgen::key(), testgen::rsa_key()] {
1213 let other = testgen::cert("inter", true, Some((&root, &rk)), &key, &san, (-1, 10));
1214 for extras in [[&other, &inter, &root], [&inter, &other, &root]] {
1215 let id = testgen::client(&leaf, &lk, &extras);
1216 assert_eq!(id.chain(), [leaf.clone(), inter.clone()]);
1217 }
1218 }
1219 }
1220
1221 #[cfg(all(unix, feature = "openssl"))]
1227 #[test]
1228 fn ecdsa_der() {
1229 use tls_openssl::bn::BigNum;
1230 use tls_openssl::ecdsa::EcdsaSig;
1231
1232 let check = |raw: Vec<u8>| {
1233 let (r, s) = raw.split_at(raw.len() / 2);
1234 let expected = EcdsaSig::from_private_components(
1235 BigNum::from_slice(r).unwrap(),
1236 BigNum::from_slice(s).unwrap(),
1237 )
1238 .unwrap()
1239 .to_der()
1240 .unwrap();
1241 assert_eq!(ecdsa_p1363_to_der(&raw), expected, "{}", hex::encode(&raw));
1242 };
1243 for len in [32, 48, 66] {
1244 check(vec![0x11; len * 2]);
1245 check(vec![0xff; len * 2]);
1246 let mut raw = vec![0u8; len * 2];
1247 raw[len - 1] = 1;
1248 raw[len] = 0x80;
1249 raw[2 * len - 1] = 7;
1250 check(raw);
1251 }
1252 }
1253}