1use openssl::ec::{EcGroup, EcKey};
5use openssl::error::ErrorStack;
6use openssl::nid::Nid;
7use openssl::rsa::Rsa;
8use openssl::x509::{
9 extension::{
10 AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage,
11 SubjectAlternativeName, SubjectKeyIdentifier,
12 },
13 X509NameBuilder, X509ReqBuilder, X509,
14};
15use openssl::{asn1, bn, hash, pkey};
16
17use crate::config::TlsConfiguration;
19use crypto_glue::{
20 pkcs8::PrivateKeyInfo, rsa::RS256PrivateKey, traits::PublicKeyParts, x509::oiddb::rfc5912,
21};
22use rustls::{
23 pki_types::{pem::PemObject, CertificateDer, CertificateRevocationListDer, PrivateKeyDer},
24 server::{ServerConfig, WebPkiClientVerifier},
25 RootCertStore,
26};
27use std::fs;
28use std::io::{Read, Write};
29use std::path::Path;
30use std::sync::Arc;
31use tokio_rustls::TlsAcceptor;
32
33const CA_VALID_DAYS: u32 = 30;
34const CERT_VALID_DAYS: u32 = 5;
35
36const RSA_MIN_KEY_SIZE_BITS: u64 = 2048;
52const EC_MIN_KEY_SIZE_BITS: u64 = 224;
53
54fn get_signing_func() -> hash::MessageDigest {
56 hash::MessageDigest::sha256()
57}
58
59pub fn check_privkey_minimums(privkey: &PrivateKeyDer<'_>) -> Result<(), String> {
61 match privkey {
62 PrivateKeyDer::Pkcs8(pkcs8_der) => {
63 let private_key_info = PrivateKeyInfo::try_from(pkcs8_der.secret_pkcs8_der())
64 .map_err(|_err| "Invalid pkcs8 der".to_string())?;
65
66 let oids = private_key_info
67 .algorithm
68 .oids()
69 .map_err(|_err| "Invalid pkcs8 key oids".to_string())?;
70
71 match oids {
72 (rfc5912::ID_EC_PUBLIC_KEY, Some(rfc5912::SECP_256_R_1)) => {
73 debug!("The EC private key size is: 256 bits, that's OK!");
74 Ok(())
75 }
76 (rfc5912::ID_EC_PUBLIC_KEY, Some(rfc5912::SECP_384_R_1)) => {
77 debug!("The EC private key size is: 384 bits, that's OK!");
78 Ok(())
79 }
80 (rfc5912::RSA_ENCRYPTION, None) => {
81 let priv_key = RS256PrivateKey::try_from(private_key_info)
82 .map_err(|_err| "Invalid rsa key".to_string())?;
83
84 let priv_key_bits = priv_key.size() * 8;
85
86 if priv_key_bits < RSA_MIN_KEY_SIZE_BITS as usize {
87 Err(format!(
88 "TLS RSA key is less than {RSA_MIN_KEY_SIZE_BITS} bits!"
89 ))
90 } else {
91 debug!(
92 "The RSA private key size is: {} bits, that's OK!",
93 priv_key_bits
94 );
95 Ok(())
96 }
97 }
98 _ => Err("TLS Private Key Oids not understood".into()),
99 }
100 }
101 _ => Err("TLS Private Key Format not understood".into()),
102 }
103}
104
105pub fn setup_tls(
108 tls_config: &Option<TlsConfiguration>,
109) -> Result<Option<TlsAcceptor>, std::io::Error> {
110 let Some(tls_param) = tls_config.as_ref() else {
111 return Ok(None);
112 };
113
114 let cert_iter = CertificateDer::pem_file_iter(&tls_param.chain)
115 .map_err(|err| std::io::Error::other(format!("Failed to create TLS listener: {err:?}")))?;
116
117 let cert_chain_der = cert_iter
118 .collect::<Result<Vec<_>, _>>()
119 .map_err(|err| std::io::Error::other(format!("Failed to create TLS listener: {err:?}")))?;
120
121 let private_key_der = PrivateKeyDer::from_pem_file(&tls_param.key)
122 .map_err(|err| std::io::Error::other(format!("Failed to create TLS listener: {err:?}")))?;
123
124 check_privkey_minimums(&private_key_der).map_err(|err| {
125 std::io::Error::other(format!("Private key minimums were not met: {err:?}"))
126 })?;
127
128 let provider: Arc<_> = rustls::crypto::aws_lc_rs::default_provider().into();
131
132 let client_cert_verifier = if let Some(client_ca) = tls_param.client_ca.as_ref() {
133 info!("Loading client certificates from {}", client_ca.display());
134
135 let read_dir = fs::read_dir(client_ca).map_err(|err| {
136 std::io::Error::other(format!(
137 "Failed to create TLS listener while loading client ca from {}: {:?}",
138 client_ca.display(),
139 err
140 ))
141 })?;
142
143 let dirents: Vec<_> = read_dir.filter_map(|item| item.ok()).collect();
144
145 let mut client_cert_roots = RootCertStore::empty();
146
147 for cert_dir_ent in dirents.iter().filter(|item| {
148 item.file_name()
149 .to_str()
150 .map(|fname| fname.ends_with(".0"))
153 .unwrap_or_default()
154 }) {
155 let cert_pem = CertificateDer::from_pem_file(cert_dir_ent.path()).map_err(|err| {
156 std::io::Error::other(format!("Failed to create TLS listener: {err:?}"))
157 })?;
158
159 client_cert_roots.add(cert_pem).map_err(|err| {
160 std::io::Error::other(format!("Failed to create TLS listener: {err:?}"))
161 })?;
162 }
163
164 let mut client_cert_crls = Vec::new();
165
166 for cert_dir_ent in dirents.iter().filter(|item| {
167 item.file_name()
168 .to_str()
169 .map(|fname| fname.ends_with(".r0"))
172 .unwrap_or_default()
173 }) {
174 let cert_pem = CertificateRevocationListDer::from_pem_file(cert_dir_ent.path())
175 .map_err(|err| {
176 std::io::Error::other(format!("Failed to create TLS listener: {err:?}"))
177 })?;
178
179 client_cert_crls.push(cert_pem);
180 }
181
182 WebPkiClientVerifier::builder_with_provider(client_cert_roots.into(), provider.clone())
183 .with_crls(client_cert_crls)
184 .allow_unauthenticated()
185 .build()
186 .map_err(|err| {
187 std::io::Error::other(format!("Failed to create TLS listener: {err:?}"))
188 })?
189 } else {
190 WebPkiClientVerifier::no_client_auth()
191 };
192
193 let tls_server_config = ServerConfig::builder_with_provider(provider)
194 .with_safe_default_protocol_versions()
195 .and_then(|builder| {
196 builder
197 .with_client_cert_verifier(client_cert_verifier)
198 .with_single_cert(cert_chain_der, private_key_der)
199 })
200 .map_err(|err| std::io::Error::other(format!("Failed to create TLS listener: {err:?}")))?;
201
202 let tls_acceptor = TlsAcceptor::from(Arc::new(tls_server_config));
203
204 Ok(Some(tls_acceptor))
205}
206
207fn get_ec_group() -> Result<EcGroup, ErrorStack> {
208 EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)
209}
210
211#[derive(Debug)]
212pub(crate) struct CaHandle {
213 key: pkey::PKey<pkey::Private>,
214 cert: X509,
215}
216
217pub(crate) fn write_ca(
218 key_ar: impl AsRef<Path>,
219 cert_ar: impl AsRef<Path>,
220 handle: &CaHandle,
221) -> Result<(), ()> {
222 let key_path: &Path = key_ar.as_ref();
223 let cert_path: &Path = cert_ar.as_ref();
224
225 let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
226 error!(err = ?e, "Failed to convert key to PEM");
227 })?;
228
229 let cert_pem = handle.cert.to_pem().map_err(|e| {
230 error!(err = ?e, "Failed to convert cert to PEM");
231 })?;
232
233 fs::File::create(key_path)
234 .and_then(|mut file| file.write_all(&key_pem))
235 .map_err(|e| {
236 error!(err = ?e, "Failed to create {:?}", key_path);
237 })?;
238
239 fs::File::create(cert_path)
240 .and_then(|mut file| file.write_all(&cert_pem))
241 .map_err(|e| {
242 error!(err = ?e, "Failed to create {:?}", cert_path);
243 })
244}
245
246#[derive(Debug)]
247pub enum KeyType {
248 #[allow(dead_code)]
249 Rsa,
250 Ec,
251}
252impl Default for KeyType {
253 fn default() -> Self {
254 Self::Ec
255 }
256}
257
258#[derive(Debug)]
259pub struct CAConfig {
260 pub key_type: KeyType,
261 pub key_bits: u64,
262 #[allow(dead_code)]
263 pub skip_enforce_minimums: bool,
264}
265
266impl Default for CAConfig {
267 fn default() -> Self {
268 #[allow(clippy::expect_used)]
269 Self::new(KeyType::Ec, 256, false)
270 .expect("Somehow the defaults failed to pass validation while building a CA Config?")
271 }
272}
273
274impl CAConfig {
275 fn new(key_type: KeyType, key_bits: u64, skip_enforce_minimums: bool) -> Result<Self, String> {
276 let res = Self {
277 key_type,
278 key_bits,
279 skip_enforce_minimums,
280 };
281 if !skip_enforce_minimums {
282 res.enforce_minimums()?;
283 };
284 Ok(res)
285 }
286
287 fn enforce_minimums(&self) -> Result<(), String> {
289 match self.key_type {
290 KeyType::Rsa => {
291 trace!(
292 "Generating CA Config for RSA Key with {} bits",
293 self.key_bits
294 );
295 if self.key_bits < RSA_MIN_KEY_SIZE_BITS {
296 return Err(format!(
297 "RSA key size must be at least {RSA_MIN_KEY_SIZE_BITS} bits"
298 ));
299 }
300 }
301 KeyType::Ec => {
302 trace!("Generating CA Config for EcKey with {} bits", self.key_bits);
303 if self.key_bits < EC_MIN_KEY_SIZE_BITS {
304 return Err(format!(
305 "EC key size must be at least {EC_MIN_KEY_SIZE_BITS} bits"
306 ));
307 }
308 }
309 };
310 Ok(())
311 }
312}
313
314pub(crate) fn gen_private_key(
315 key_type: &KeyType,
316 key_bits: Option<u64>,
317) -> Result<pkey::PKey<pkey::Private>, ErrorStack> {
318 match key_type {
319 KeyType::Rsa => {
320 let key_bits = key_bits.unwrap_or(RSA_MIN_KEY_SIZE_BITS);
321 let rsa = Rsa::generate(key_bits as u32)?;
322 pkey::PKey::from_rsa(rsa)
323 }
324 KeyType::Ec => {
325 let ecgroup = get_ec_group()?;
327 let eckey = EcKey::generate(&ecgroup)?;
328 pkey::PKey::from_ec_key(eckey)
329 }
330 }
331}
332
333pub(crate) fn build_ca(ca_config: Option<CAConfig>) -> Result<CaHandle, ErrorStack> {
335 let ca_config = ca_config.unwrap_or_default();
338
339 let ca_key = gen_private_key(&ca_config.key_type, Some(ca_config.key_bits))?;
341
342 let mut x509_name = X509NameBuilder::new()?;
343
344 x509_name.append_entry_by_text("C", "AU")?;
345 x509_name.append_entry_by_text("ST", "QLD")?;
346 x509_name.append_entry_by_text("O", "Kanidm")?;
347 x509_name.append_entry_by_text("CN", "Kanidm Generated CA")?;
348 x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
349 let x509_name = x509_name.build();
350
351 let mut cert_builder = X509::builder()?;
352 cert_builder.set_version(2)?;
354
355 let serial_number = bn::BigNum::from_u32(1).and_then(|serial| serial.to_asn1_integer())?;
356
357 cert_builder.set_serial_number(&serial_number)?;
358 cert_builder.set_subject_name(&x509_name)?;
359 cert_builder.set_issuer_name(&x509_name)?;
360
361 let not_before = asn1::Asn1Time::days_from_now(0)?;
362 cert_builder.set_not_before(¬_before)?;
363 let not_after = asn1::Asn1Time::days_from_now(CA_VALID_DAYS)?;
364 cert_builder.set_not_after(¬_after)?;
365
366 cert_builder.append_extension(BasicConstraints::new().critical().ca().pathlen(0).build()?)?;
367 cert_builder.append_extension(
368 KeyUsage::new()
369 .critical()
370 .key_cert_sign()
371 .crl_sign()
372 .build()?,
373 )?;
374
375 let subject_key_identifier =
376 SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
377 cert_builder.append_extension(subject_key_identifier)?;
378
379 cert_builder.set_pubkey(&ca_key)?;
380
381 cert_builder.sign(&ca_key, get_signing_func())?;
382 let ca_cert = cert_builder.build();
383
384 Ok(CaHandle {
385 key: ca_key,
386 cert: ca_cert,
387 })
388}
389
390pub(crate) fn load_ca(
391 ca_key_ar: impl AsRef<Path>,
392 ca_cert_ar: impl AsRef<Path>,
393) -> Result<CaHandle, ()> {
394 let ca_key_path: &Path = ca_key_ar.as_ref();
395 let ca_cert_path: &Path = ca_cert_ar.as_ref();
396
397 let mut ca_key_pem = vec![];
398 fs::File::open(ca_key_path)
399 .and_then(|mut file| file.read_to_end(&mut ca_key_pem))
400 .map_err(|e| {
401 error!(err = ?e, "Failed to read {:?}", ca_key_path);
402 })?;
403
404 let mut ca_cert_pem = vec![];
405 fs::File::open(ca_cert_path)
406 .and_then(|mut file| file.read_to_end(&mut ca_cert_pem))
407 .map_err(|e| {
408 error!(err = ?e, "Failed to read {:?}", ca_cert_path);
409 })?;
410
411 let ca_key = pkey::PKey::private_key_from_pem(&ca_key_pem).map_err(|e| {
412 error!(err = ?e, "Failed to convert PEM to key");
413 })?;
414
415 let ca_cert = X509::from_pem(&ca_cert_pem).map_err(|e| {
424 error!(err = ?e, "Failed to convert PEM to cert");
425 })?;
426
427 Ok(CaHandle {
428 key: ca_key,
429 cert: ca_cert,
430 })
431}
432
433pub(crate) struct CertHandle {
434 key: pkey::PKey<pkey::Private>,
435 cert: X509,
436 chain: Vec<X509>,
437}
438
439pub(crate) fn write_cert(
440 key_ar: impl AsRef<Path>,
441 chain_ar: impl AsRef<Path>,
442 cert_ar: impl AsRef<Path>,
443 handle: &CertHandle,
444) -> Result<(), ()> {
445 let key_path: &Path = key_ar.as_ref();
446 let chain_path: &Path = chain_ar.as_ref();
447 let cert_path: &Path = cert_ar.as_ref();
448
449 let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
450 error!(err = ?e, "Failed to convert key to PEM");
451 })?;
452
453 let cert_pem = handle.cert.to_pem().map_err(|e| {
454 error!(err = ?e, "Failed to convert cert to PEM");
455 })?;
456
457 let mut chain_pem = cert_pem.clone();
458
459 for ca_cert in &handle.chain {
461 match ca_cert.to_pem() {
462 Ok(c) => {
463 chain_pem.extend_from_slice(&c);
464 }
465 Err(e) => {
466 error!(err = ?e, "Failed to convert cert to PEM");
467 return Err(());
468 }
469 }
470 }
471
472 fs::File::create(key_path)
473 .and_then(|mut file| file.write_all(&key_pem))
474 .map_err(|e| {
475 error!(err = ?e, "Failed to create {:?}", key_path);
476 })?;
477
478 fs::File::create(chain_path)
479 .and_then(|mut file| file.write_all(&chain_pem))
480 .map_err(|e| {
481 error!(err = ?e, "Failed to create {:?}", chain_path);
482 })?;
483
484 fs::File::create(cert_path)
485 .and_then(|mut file| file.write_all(&cert_pem))
486 .map_err(|e| {
487 error!(err = ?e, "Failed to create {:?}", cert_path);
488 })
489}
490
491pub(crate) fn build_cert(
492 domain_name: &str,
493 ca_handle: &CaHandle,
494 key_type: Option<KeyType>,
495 key_bits: Option<u64>,
496) -> Result<CertHandle, ErrorStack> {
497 let key_type = key_type.unwrap_or_default();
498 let int_key = gen_private_key(&key_type, key_bits)?;
499
500 let mut req_builder = X509ReqBuilder::new()?;
501 req_builder.set_pubkey(&int_key)?;
502
503 let mut x509_name = X509NameBuilder::new()?;
504 x509_name.append_entry_by_text("C", "AU")?;
505 x509_name.append_entry_by_text("ST", "QLD")?;
506 x509_name.append_entry_by_text("O", "Kanidm")?;
507 x509_name.append_entry_by_text("CN", domain_name)?;
508 x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
510 let x509_name = x509_name.build();
511
512 req_builder.set_subject_name(&x509_name)?;
513 req_builder.sign(&int_key, get_signing_func())?;
514 let req = req_builder.build();
515 let mut cert_builder = X509::builder()?;
518 cert_builder.set_version(2)?;
520 let serial_number = bn::BigNum::from_u32(2).and_then(|serial| serial.to_asn1_integer())?;
521
522 cert_builder.set_pubkey(&int_key)?;
523
524 cert_builder.set_serial_number(&serial_number)?;
525 cert_builder.set_subject_name(req.subject_name())?;
526 cert_builder.set_issuer_name(ca_handle.cert.subject_name())?;
527
528 let not_before = asn1::Asn1Time::days_from_now(0)?;
529 cert_builder.set_not_before(¬_before)?;
530 let not_after = asn1::Asn1Time::days_from_now(CERT_VALID_DAYS)?;
531 cert_builder.set_not_after(¬_after)?;
532
533 cert_builder.append_extension(BasicConstraints::new().build()?)?;
534
535 cert_builder.append_extension(
536 KeyUsage::new()
537 .critical()
538 .digital_signature()
539 .key_encipherment()
540 .build()?,
541 )?;
542
543 cert_builder.append_extension(
544 ExtendedKeyUsage::new()
545 .server_auth()
547 .build()?,
548 )?;
549
550 let subject_key_identifier = SubjectKeyIdentifier::new()
551 .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
552 cert_builder.append_extension(subject_key_identifier)?;
553
554 let auth_key_identifier = AuthorityKeyIdentifier::new()
555 .keyid(false)
556 .issuer(false)
557 .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
558 cert_builder.append_extension(auth_key_identifier)?;
559
560 let subject_alt_name = SubjectAlternativeName::new()
561 .dns(domain_name)
562 .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
563 cert_builder.append_extension(subject_alt_name)?;
564
565 cert_builder.sign(&ca_handle.key, get_signing_func())?;
566 let int_cert = cert_builder.build();
567
568 Ok(CertHandle {
569 key: int_key,
570 cert: int_cert,
571 chain: vec![ca_handle.cert.clone()],
572 })
573}
574
575#[test]
576fn test_enforced_minimums() {
578 let good_ca_configs = vec![
579 (KeyType::Rsa, 4096, false),
581 (KeyType::Rsa, 2048, false),
583 (KeyType::Ec, 256, false),
585 ];
586 good_ca_configs.into_iter().for_each(|config| {
587 dbg!(&config);
588 assert!(CAConfig::new(config.0, config.1, config.2).is_ok());
589 });
590 }
603
604#[test]
605fn test_ca_loader() {
606 let ca_key_tempfile = tempfile::NamedTempFile::new().unwrap();
607 let ca_cert_tempfile = tempfile::NamedTempFile::new().unwrap();
608 let ca_config = CAConfig::default();
611 if let Ok(ca) = build_ca(Some(ca_config)) {
612 write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
613 assert!(load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path()).is_ok());
614 };
615
616 let good_ca_configs = vec![
617 (KeyType::Rsa, 4096, false),
619 (KeyType::Rsa, 2048, false),
621 (KeyType::Ec, 256, false),
623 ];
624 good_ca_configs.into_iter().for_each(|config| {
625 println!("testing good config {config:?}");
626 let ca_config = CAConfig::new(config.0, config.1, config.2).unwrap();
627 let ca = build_ca(Some(ca_config)).unwrap();
628 write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
629 let ca_result = load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path());
630 println!("result: {ca_result:?}");
631 assert!(ca_result.is_ok());
632 });
633
634 }