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