authly_client/
identity.rs1use std::{borrow::Cow, str::FromStr};
4
5use authly_common::id::ServiceId;
6use pem::{EncodeConfig, Pem};
7
8use crate::Error;
9
10#[derive(Clone)]
14pub struct Identity {
15 pub(crate) cert_pem: Vec<u8>,
16 pub(crate) key_pem: Vec<u8>,
17}
18
19impl Identity {
20 pub fn from_pem(pem: impl AsRef<[u8]>) -> Result<Self, Error> {
22 use rustls_pemfile::Item;
23 use std::io::Cursor;
24
25 let mut pem = Cursor::new(pem);
26 let mut certs = Vec::<rustls_pki_types::CertificateDer>::new();
27 let mut keys = Vec::<rustls_pki_types::PrivateKeyDer>::new();
28
29 for result in rustls_pemfile::read_all(&mut pem) {
30 match result {
31 Ok(Item::X509Certificate(cert)) => certs.push(cert),
32 Ok(Item::Pkcs1Key(key)) => keys.push(key.into()),
33 Ok(Item::Pkcs8Key(key)) => keys.push(key.into()),
34 Ok(Item::Sec1Key(key)) => keys.push(key.into()),
35 Ok(_) => {
36 return Err(Error::Identity("No valid certificate was found"));
37 }
38 Err(_) => {
39 return Err(Error::Identity("Invalid identity PEM file"));
40 }
41 }
42 }
43
44 let Some(cert) = certs.into_iter().next() else {
45 return Err(Error::Identity("Certificate not found"));
46 };
47 let Some(key) = keys.into_iter().next() else {
48 return Err(Error::Identity("Private key not found"));
49 };
50
51 Ok(Self {
52 cert_pem: pem::encode_config(
53 &Pem::new("CERTIFICATE", cert.to_vec()),
54 EncodeConfig::new().set_line_ending(pem::LineEnding::LF),
55 )
56 .into_bytes(),
57 key_pem: pem::encode_config(
58 &Pem::new("PRIVATE KEY", key.secret_der()),
59 EncodeConfig::new().set_line_ending(pem::LineEnding::LF),
60 )
61 .into_bytes(),
62 })
63 }
64
65 pub fn cert_pem(&self) -> Cow<[u8]> {
67 self.cert_pem.as_slice().into()
68 }
69
70 pub fn key_pem(&self) -> Cow<[u8]> {
72 self.key_pem.as_slice().into()
73 }
74
75 pub fn pem(&self) -> Result<Cow<[u8]>, Error> {
77 let mut identity_pem = self.cert_pem.clone();
78 identity_pem.extend(&self.key_pem);
79 Ok(Cow::Owned(identity_pem))
80 }
81}
82
83#[derive(Clone)]
84pub(crate) struct IdentityData {
85 pub entity_id: ServiceId,
86}
87
88pub(crate) fn parse_identity_data(cert: &[u8]) -> Result<IdentityData, Error> {
89 let pem = pem::parse(cert).map_err(|_| Error::AuthlyCA("invalid authly certificate"))?;
90
91 let (_, x509_cert) = x509_parser::parse_x509_certificate(pem.contents())
92 .map_err(|_| Error::AuthlyCA("invalid authly certificate"))?;
93
94 let mut entity_id: Option<ServiceId> = None;
95
96 for subject_attr in x509_cert.subject().iter_attributes() {
97 if let Some(oid_iter) = subject_attr.attr_type().iter() {
98 if oid_iter.eq(authly_common::certificate::oid::ENTITY_UNIQUE_IDENTIFIER
99 .iter()
100 .copied())
101 {
102 let value = subject_attr
103 .attr_value()
104 .as_str()
105 .map_err(|_| Error::Identity("Entity Id value encoding"))?;
106 entity_id = Some(
107 ServiceId::from_str(value)
108 .map_err(|_| Error::Identity("Entity Id value encoding"))?,
109 );
110 }
111 }
112 }
113
114 let entity_id = entity_id.ok_or_else(|| Error::Identity("Entity Id is missing"))?;
115
116 Ok(IdentityData { entity_id })
118}