kanidm_lib_crypto/
mtls.rs1use crate::CryptoError;
2use openssl::asn1;
3use openssl::bn;
4use openssl::ec;
5use openssl::error::ErrorStack as OpenSSLError;
6use openssl::hash;
7use openssl::nid::Nid;
8use openssl::pkey::{PKey, Private};
9use openssl::x509::extension::BasicConstraints;
10use openssl::x509::extension::ExtendedKeyUsage;
11use openssl::x509::extension::KeyUsage;
12use openssl::x509::extension::SubjectAlternativeName;
13use openssl::x509::extension::SubjectKeyIdentifier;
14use openssl::x509::X509NameBuilder;
15use openssl::x509::X509;
16use rustls::pki_types::ServerName;
17use uuid::Uuid;
18
19pub fn get_group() -> Result<ec::EcGroup, OpenSSLError> {
21 ec::EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)
22}
23
24pub fn build_self_signed_server_and_client_identity(
25 cn: Uuid,
26 domain_name: &str,
27 expiration_days: u32,
28) -> Result<(PKey<Private>, X509), CryptoError> {
29 let ecgroup = get_group()?;
30 let eckey = ec::EcKey::generate(&ecgroup)?;
31 let ca_key = PKey::from_ec_key(eckey)?;
32 let mut x509_name = X509NameBuilder::new()?;
33
34 x509_name.append_entry_by_text("O", "Kanidm Replication")?;
37 x509_name.append_entry_by_text("CN", &cn.as_hyphenated().to_string())?;
38 let x509_name = x509_name.build();
39
40 let mut cert_builder = X509::builder()?;
41 cert_builder.set_version(2)?;
43
44 let serial_number = bn::BigNum::from_u32(1).and_then(|serial| serial.to_asn1_integer())?;
45
46 cert_builder.set_serial_number(&serial_number)?;
47 cert_builder.set_subject_name(&x509_name)?;
48 cert_builder.set_issuer_name(&x509_name)?;
49
50 let not_before = asn1::Asn1Time::days_from_now(0)?;
51 cert_builder.set_not_before(¬_before)?;
52 let not_after = asn1::Asn1Time::days_from_now(expiration_days)?;
53 cert_builder.set_not_after(¬_after)?;
54
55 cert_builder.append_extension(BasicConstraints::new().critical().build()?)?;
57 cert_builder.append_extension(
58 KeyUsage::new()
59 .critical()
60 .digital_signature()
61 .key_encipherment()
62 .build()?,
63 )?;
64
65 cert_builder.append_extension(
66 ExtendedKeyUsage::new()
67 .server_auth()
68 .client_auth()
69 .build()?,
70 )?;
71
72 let subject_key_identifier =
73 SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
74 cert_builder.append_extension(subject_key_identifier)?;
75
76 let Ok(server_name) = ServerName::try_from(domain_name.to_owned()) else {
78 return Err(CryptoError::InvalidServerName);
79 };
80
81 let subject_alt_name = match server_name {
82 ServerName::DnsName(_) => SubjectAlternativeName::new()
83 .dns(domain_name)
84 .build(&cert_builder.x509v3_context(None, None))?,
85 ServerName::IpAddress(_) => SubjectAlternativeName::new()
86 .ip(domain_name)
87 .build(&cert_builder.x509v3_context(None, None))?,
88 _ => return Err(CryptoError::InvalidServerName),
89 };
90
91 cert_builder.append_extension(subject_alt_name)?;
92
93 cert_builder.set_pubkey(&ca_key)?;
94
95 cert_builder.sign(&ca_key, hash::MessageDigest::sha256())?;
96 let ca_cert = cert_builder.build();
97
98 Ok((ca_key, ca_cert))
99}