kanidmd_core/
crypto.rs

1//! This module contains cryptographic setup code, a long with what policy
2//! and ciphers we accept.
3
4use openssl::ec::{EcGroup, EcKey};
5use openssl::error::ErrorStack;
6use openssl::nid::Nid;
7use openssl::pkey::{PKeyRef, Private};
8use openssl::rsa::Rsa;
9use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslSessionCacheMode, SslVerifyMode};
10use openssl::x509::{
11    extension::{
12        AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage,
13        SubjectAlternativeName, SubjectKeyIdentifier,
14    },
15    X509NameBuilder, X509ReqBuilder, X509,
16};
17use openssl::{asn1, bn, hash, pkey};
18use sketching::*;
19
20use crate::config::TlsConfiguration;
21
22use std::fs;
23use std::io::{Read, Write};
24use std::path::Path;
25
26const CA_VALID_DAYS: u32 = 30;
27const CERT_VALID_DAYS: u32 = 5;
28
29// Basing minimums off https://www.keylength.com setting "year" to 2030 - tested as at 2023-09-25
30//
31// |Method           |Date     |Symmetric| FM      |DL Key| DL Group|Elliptic Curve|Hash|
32// |   ---           |   ---   |   ---   |   ---   | ---  |   ---   |  ---         | ---|
33// |Lenstra / Verheul|2030     |  93     |2493^2016|165   | 2493    |  176         | 186|
34// |Lenstra Updated  |2030     |  88     |1698^2063|176   | 1698    |  176         | 176|
35// |ECRYPT           |2029-2068|  256    |15360    |512   | 15360   |  512         | 512|
36// |NIST             |2019-2030|  112    |2048     |224   | 2048    |  224         | 224|
37// |ANSSI            |> 2030   |  128    |3072     |200   | 3072    |  256         | 256|
38// |NSA              |-        |  256    |3072     |-     | -       |  384         | 384|
39// |RFC3766          |-        |  -      |   -     | -    |   -     |   -          |  - |
40// |BSI              |-        |  -      |   -     | -    |   -     |   -          |  - |
41// DL - Discrete Logarithm
42// FM - Factoring Modulus
43
44const RSA_MIN_KEY_SIZE_BITS: u64 = 2048;
45const EC_MIN_KEY_SIZE_BITS: u64 = 224;
46
47/// returns a signing function that meets a sensible minimum
48fn get_signing_func() -> hash::MessageDigest {
49    hash::MessageDigest::sha256()
50}
51
52/// Ensure we're enforcing safe minimums for TLS keys
53pub fn check_privkey_minimums(privkey: &PKeyRef<Private>) -> Result<(), String> {
54    if let Ok(key) = privkey.rsa() {
55        if key.size() < (RSA_MIN_KEY_SIZE_BITS / 8) as u32 {
56            Err(format!(
57                "TLS RSA key is less than {} bits!",
58                RSA_MIN_KEY_SIZE_BITS
59            ))
60        } else {
61            debug!(
62                "The RSA private key size is: {} bits, that's OK!",
63                key.size() * 8
64            );
65            Ok(())
66        }
67    } else if let Ok(key) = privkey.ec_key() {
68        // allowing this to panic because ... it's an i32 and hopefully we don't have negative bit lengths?
69        #[allow(clippy::panic)]
70        let key_bits: u64 = key.private_key().num_bits().try_into().unwrap_or_else(|_| {
71            panic!(
72                "Failed to convert EC bitlength {} to u64",
73                key.private_key().num_bits()
74            )
75        });
76
77        if key_bits < EC_MIN_KEY_SIZE_BITS {
78            Err(format!(
79                "TLS EC key is less than {} bits! Got: {}",
80                EC_MIN_KEY_SIZE_BITS, key_bits
81            ))
82        } else {
83            #[cfg(any(test, debug_assertions))]
84            println!("The EC private key size is: {} bits, that's OK!", key_bits);
85            debug!("The EC private key size is: {} bits, that's OK!", key_bits);
86            Ok(())
87        }
88    } else {
89        error!("TLS key is not RSA or EC, cannot check minimums!");
90        Ok(())
91    }
92}
93
94/// From the server configuration, generate an OpenSSL acceptor that we can use
95/// to build our sockets for HTTPS/LDAPS.
96pub fn setup_tls(
97    tls_config: &Option<TlsConfiguration>,
98) -> Result<Option<SslAcceptor>, std::io::Error> {
99    let Some(tls_param) = tls_config.as_ref() else {
100        return Ok(None);
101    };
102
103    let mut tls_builder = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls())?;
104
105    tls_builder
106        .set_certificate_chain_file(tls_param.chain.clone())
107        .map_err(|err| {
108            std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
109        })?;
110
111    tls_builder
112        .set_private_key_file(tls_param.key.clone(), SslFiletype::PEM)
113        .map_err(|err| {
114            std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
115        })?;
116
117    tls_builder.check_private_key().map_err(|err| {
118        std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
119    })?;
120
121    // If configured, setup TLS client authentication.
122    if let Some(client_ca) = tls_param.client_ca.as_ref() {
123        info!("Loading client certificates from {}", client_ca.display());
124
125        let verify = SslVerifyMode::PEER;
126        // In future we may add a "require mTLS option" which would necessitate this.
127        // verify.insert(SslVerifyMode::FAIL_IF_NO_PEER_CERT);
128        tls_builder.set_verify(verify);
129
130        // When client certs are available, we disable the TLS session cache.
131        // This is so that when the smartcard is *removed* on the client, it forces
132        // the client session to immediately expire.
133        //
134        // https://stackoverflow.com/questions/12393711/session-disconnect-the-client-after-smart-card-is-removed
135        //
136        // Alternately, on logout we need to trigger https://docs.rs/openssl/latest/openssl/ssl/struct.Ssl.html#method.set_ssl_context
137        // with https://docs.rs/openssl/latest/openssl/ssl/struct.Ssl.html#method.ssl_context +
138        // https://docs.rs/openssl/latest/openssl/ssl/struct.SslContextRef.html#method.remove_session
139        //
140        // Or we lower session time outs etc.
141        tls_builder.set_session_cache_mode(SslSessionCacheMode::OFF);
142
143        let read_dir = fs::read_dir(client_ca).map_err(|err| {
144            std::io::Error::other(format!(
145                "Failed to create TLS listener while loading client ca from {}: {:?}",
146                client_ca.display(),
147                err
148            ))
149        })?;
150
151        for cert_dir_ent in read_dir.filter_map(|item| item.ok()).filter(|item| {
152            item.file_name()
153                .to_str()
154                // Hashed certs end in .0
155                // Hashed crls are .r0
156                .map(|fname| fname.ends_with(".0"))
157                .unwrap_or_default()
158        }) {
159            let mut cert_pem = String::new();
160            fs::File::open(cert_dir_ent.path())
161                .and_then(|mut file| file.read_to_string(&mut cert_pem))
162                .map_err(|err| {
163                    std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
164                })?;
165
166            let cert = X509::from_pem(cert_pem.as_bytes()).map_err(|err| {
167                std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
168            })?;
169
170            let cert_store = tls_builder.cert_store_mut();
171            cert_store.add_cert(cert.clone()).map_err(|err| {
172                std::io::Error::other(format!(
173                    "Failed to load cert store while creating TLS listener: {:?}",
174                    err
175                ))
176            })?;
177            // This tells the client what CA's they should use. It DOES NOT
178            // verify them. That's the job of the cert store above!
179            tls_builder.add_client_ca(&cert).map_err(|err| {
180                std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
181            })?;
182        }
183
184        // TODO: Build our own CRL map HERE!
185
186        // Allow dumping client cert chains for dev debugging
187        // In the case this is status=false, should we be dumping these anyway?
188        if enabled!(tracing::Level::TRACE) {
189            tls_builder.set_verify_callback(verify, |status, x509store| {
190                if let Some(current_cert) = x509store.current_cert() {
191                    let cert_text_bytes = current_cert.to_text().unwrap_or_default();
192                    let cert_text = String::from_utf8_lossy(cert_text_bytes.as_slice());
193                    tracing::warn!(client_cert = %cert_text);
194                };
195
196                if let Some(chain) = x509store.chain() {
197                    for cert in chain.iter() {
198                        let cert_text_bytes = cert.to_text().unwrap_or_default();
199                        let cert_text = String::from_utf8_lossy(cert_text_bytes.as_slice());
200                        tracing::warn!(chain_cert = %cert_text);
201                    }
202                }
203
204                status
205            });
206        }
207
208        // End tls_client setup
209    }
210
211    let tls_acceptor = tls_builder.build();
212
213    // let's enforce some TLS minimums!
214    let privkey = tls_acceptor.context().private_key().ok_or_else(|| {
215        std::io::Error::other("Failed to access tls_acceptor private key".to_string())
216    })?;
217
218    check_privkey_minimums(privkey).map_err(|err| {
219        std::io::Error::other(format!("Private key minimums were not met: {:?}", err))
220    })?;
221
222    Ok(Some(tls_acceptor))
223}
224
225fn get_ec_group() -> Result<EcGroup, ErrorStack> {
226    EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)
227}
228
229#[derive(Debug)]
230pub(crate) struct CaHandle {
231    key: pkey::PKey<pkey::Private>,
232    cert: X509,
233}
234
235pub(crate) fn write_ca(
236    key_ar: impl AsRef<Path>,
237    cert_ar: impl AsRef<Path>,
238    handle: &CaHandle,
239) -> Result<(), ()> {
240    let key_path: &Path = key_ar.as_ref();
241    let cert_path: &Path = cert_ar.as_ref();
242
243    let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
244        error!(err = ?e, "Failed to convert key to PEM");
245    })?;
246
247    let cert_pem = handle.cert.to_pem().map_err(|e| {
248        error!(err = ?e, "Failed to convert cert to PEM");
249    })?;
250
251    fs::File::create(key_path)
252        .and_then(|mut file| file.write_all(&key_pem))
253        .map_err(|e| {
254            error!(err = ?e, "Failed to create {:?}", key_path);
255        })?;
256
257    fs::File::create(cert_path)
258        .and_then(|mut file| file.write_all(&cert_pem))
259        .map_err(|e| {
260            error!(err = ?e, "Failed to create {:?}", cert_path);
261        })
262}
263
264#[derive(Debug)]
265pub enum KeyType {
266    #[allow(dead_code)]
267    Rsa,
268    Ec,
269}
270impl Default for KeyType {
271    fn default() -> Self {
272        Self::Ec
273    }
274}
275
276#[derive(Debug)]
277pub struct CAConfig {
278    pub key_type: KeyType,
279    pub key_bits: u64,
280    pub skip_enforce_minimums: bool,
281}
282
283impl Default for CAConfig {
284    fn default() -> Self {
285        #[allow(clippy::expect_used)]
286        Self::new(KeyType::Ec, 256, false)
287            .expect("Somehow the defaults failed to pass validation while building a CA Config?")
288    }
289}
290
291impl CAConfig {
292    fn new(key_type: KeyType, key_bits: u64, skip_enforce_minimums: bool) -> Result<Self, String> {
293        let res = Self {
294            key_type,
295            key_bits,
296            skip_enforce_minimums,
297        };
298        if !skip_enforce_minimums {
299            res.enforce_minimums()?;
300        };
301        Ok(res)
302    }
303
304    /// Make sure we're meeting the minimum spec for key length etc
305    fn enforce_minimums(&self) -> Result<(), String> {
306        match self.key_type {
307            KeyType::Rsa => {
308                trace!(
309                    "Generating CA Config for RSA Key with {} bits",
310                    self.key_bits
311                );
312                if self.key_bits < RSA_MIN_KEY_SIZE_BITS {
313                    return Err(format!(
314                        "RSA key size must be at least {} bits",
315                        RSA_MIN_KEY_SIZE_BITS
316                    ));
317                }
318            }
319            KeyType::Ec => {
320                trace!("Generating CA Config for EcKey with {} bits", self.key_bits);
321                if self.key_bits < EC_MIN_KEY_SIZE_BITS {
322                    return Err(format!(
323                        "EC key size must be at least {} bits",
324                        EC_MIN_KEY_SIZE_BITS
325                    ));
326                }
327            }
328        };
329        Ok(())
330    }
331}
332
333pub(crate) fn gen_private_key(
334    key_type: &KeyType,
335    key_bits: Option<u64>,
336) -> Result<pkey::PKey<pkey::Private>, ErrorStack> {
337    match key_type {
338        KeyType::Rsa => {
339            let key_bits = key_bits.unwrap_or(RSA_MIN_KEY_SIZE_BITS);
340            let rsa = Rsa::generate(key_bits as u32)?;
341            pkey::PKey::from_rsa(rsa)
342        }
343        KeyType::Ec => {
344            // TODO: take key bitlength and use it for the curve group, somehow?
345            let ecgroup = get_ec_group()?;
346            let eckey = EcKey::generate(&ecgroup)?;
347            pkey::PKey::from_ec_key(eckey)
348        }
349    }
350}
351
352/// build up a CA certificate and key.
353pub(crate) fn build_ca(ca_config: Option<CAConfig>) -> Result<CaHandle, ErrorStack> {
354    let ca_config = ca_config.unwrap_or_default();
355
356    let ca_key = gen_private_key(&ca_config.key_type, Some(ca_config.key_bits))?;
357
358    if !ca_config.skip_enforce_minimums {
359        check_privkey_minimums(&ca_key).map_err(|err| {
360            admin_error!("failed to build_ca due to privkey minimums {}", err);
361            #[cfg(any(test, debug_assertions))]
362            println!("failed to build_ca due to privkey minimums: {}", err);
363            ErrorStack::get() // this probably should be a real errorstack but... how?
364        })?;
365    }
366    let mut x509_name = X509NameBuilder::new()?;
367
368    x509_name.append_entry_by_text("C", "AU")?;
369    x509_name.append_entry_by_text("ST", "QLD")?;
370    x509_name.append_entry_by_text("O", "Kanidm")?;
371    x509_name.append_entry_by_text("CN", "Kanidm Generated CA")?;
372    x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
373    let x509_name = x509_name.build();
374
375    let mut cert_builder = X509::builder()?;
376    // Yes, 2 actually means 3 here ...
377    cert_builder.set_version(2)?;
378
379    let serial_number = bn::BigNum::from_u32(1).and_then(|serial| serial.to_asn1_integer())?;
380
381    cert_builder.set_serial_number(&serial_number)?;
382    cert_builder.set_subject_name(&x509_name)?;
383    cert_builder.set_issuer_name(&x509_name)?;
384
385    let not_before = asn1::Asn1Time::days_from_now(0)?;
386    cert_builder.set_not_before(&not_before)?;
387    let not_after = asn1::Asn1Time::days_from_now(CA_VALID_DAYS)?;
388    cert_builder.set_not_after(&not_after)?;
389
390    cert_builder.append_extension(BasicConstraints::new().critical().ca().pathlen(0).build()?)?;
391    cert_builder.append_extension(
392        KeyUsage::new()
393            .critical()
394            .key_cert_sign()
395            .crl_sign()
396            .build()?,
397    )?;
398
399    let subject_key_identifier =
400        SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
401    cert_builder.append_extension(subject_key_identifier)?;
402
403    cert_builder.set_pubkey(&ca_key)?;
404
405    cert_builder.sign(&ca_key, get_signing_func())?;
406    let ca_cert = cert_builder.build();
407
408    Ok(CaHandle {
409        key: ca_key,
410        cert: ca_cert,
411    })
412}
413
414pub(crate) fn load_ca(
415    ca_key_ar: impl AsRef<Path>,
416    ca_cert_ar: impl AsRef<Path>,
417) -> Result<CaHandle, ()> {
418    let ca_key_path: &Path = ca_key_ar.as_ref();
419    let ca_cert_path: &Path = ca_cert_ar.as_ref();
420
421    let mut ca_key_pem = vec![];
422    fs::File::open(ca_key_path)
423        .and_then(|mut file| file.read_to_end(&mut ca_key_pem))
424        .map_err(|e| {
425            error!(err = ?e, "Failed to read {:?}", ca_key_path);
426        })?;
427
428    let mut ca_cert_pem = vec![];
429    fs::File::open(ca_cert_path)
430        .and_then(|mut file| file.read_to_end(&mut ca_cert_pem))
431        .map_err(|e| {
432            error!(err = ?e, "Failed to read {:?}", ca_cert_path);
433        })?;
434
435    let ca_key = pkey::PKey::private_key_from_pem(&ca_key_pem).map_err(|e| {
436        error!(err = ?e, "Failed to convert PEM to key");
437    })?;
438
439    check_privkey_minimums(&ca_key).map_err(|err| {
440        #[cfg(any(test, debug_assertions))]
441        println!("{:?}", err);
442        admin_error!("{}", err);
443    })?;
444
445    let ca_cert = X509::from_pem(&ca_cert_pem).map_err(|e| {
446        error!(err = ?e, "Failed to convert PEM to cert");
447    })?;
448
449    Ok(CaHandle {
450        key: ca_key,
451        cert: ca_cert,
452    })
453}
454
455pub(crate) struct CertHandle {
456    key: pkey::PKey<pkey::Private>,
457    cert: X509,
458    chain: Vec<X509>,
459}
460
461pub(crate) fn write_cert(
462    key_ar: impl AsRef<Path>,
463    chain_ar: impl AsRef<Path>,
464    cert_ar: impl AsRef<Path>,
465    handle: &CertHandle,
466) -> Result<(), ()> {
467    let key_path: &Path = key_ar.as_ref();
468    let chain_path: &Path = chain_ar.as_ref();
469    let cert_path: &Path = cert_ar.as_ref();
470
471    let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
472        error!(err = ?e, "Failed to convert key to PEM");
473    })?;
474
475    let cert_pem = handle.cert.to_pem().map_err(|e| {
476        error!(err = ?e, "Failed to convert cert to PEM");
477    })?;
478
479    let mut chain_pem = cert_pem.clone();
480
481    // Build the chain PEM.
482    for ca_cert in &handle.chain {
483        match ca_cert.to_pem() {
484            Ok(c) => {
485                chain_pem.extend_from_slice(&c);
486            }
487            Err(e) => {
488                error!(err = ?e, "Failed to convert cert to PEM");
489                return Err(());
490            }
491        }
492    }
493
494    fs::File::create(key_path)
495        .and_then(|mut file| file.write_all(&key_pem))
496        .map_err(|e| {
497            error!(err = ?e, "Failed to create {:?}", key_path);
498        })?;
499
500    fs::File::create(chain_path)
501        .and_then(|mut file| file.write_all(&chain_pem))
502        .map_err(|e| {
503            error!(err = ?e, "Failed to create {:?}", chain_path);
504        })?;
505
506    fs::File::create(cert_path)
507        .and_then(|mut file| file.write_all(&cert_pem))
508        .map_err(|e| {
509            error!(err = ?e, "Failed to create {:?}", cert_path);
510        })
511}
512
513pub(crate) fn build_cert(
514    domain_name: &str,
515    ca_handle: &CaHandle,
516    key_type: Option<KeyType>,
517    key_bits: Option<u64>,
518) -> Result<CertHandle, ErrorStack> {
519    let key_type = key_type.unwrap_or_default();
520    let int_key = gen_private_key(&key_type, key_bits)?;
521
522    let mut req_builder = X509ReqBuilder::new()?;
523    req_builder.set_pubkey(&int_key)?;
524
525    let mut x509_name = X509NameBuilder::new()?;
526    x509_name.append_entry_by_text("C", "AU")?;
527    x509_name.append_entry_by_text("ST", "QLD")?;
528    x509_name.append_entry_by_text("O", "Kanidm")?;
529    x509_name.append_entry_by_text("CN", domain_name)?;
530    // Requirement of packed attestation.
531    x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
532    let x509_name = x509_name.build();
533
534    req_builder.set_subject_name(&x509_name)?;
535    req_builder.sign(&int_key, get_signing_func())?;
536    let req = req_builder.build();
537    // ==
538
539    let mut cert_builder = X509::builder()?;
540    // Yes, 2 actually means 3 here ...
541    cert_builder.set_version(2)?;
542    let serial_number = bn::BigNum::from_u32(2).and_then(|serial| serial.to_asn1_integer())?;
543
544    cert_builder.set_pubkey(&int_key)?;
545
546    cert_builder.set_serial_number(&serial_number)?;
547    cert_builder.set_subject_name(req.subject_name())?;
548    cert_builder.set_issuer_name(ca_handle.cert.subject_name())?;
549
550    let not_before = asn1::Asn1Time::days_from_now(0)?;
551    cert_builder.set_not_before(&not_before)?;
552    let not_after = asn1::Asn1Time::days_from_now(CERT_VALID_DAYS)?;
553    cert_builder.set_not_after(&not_after)?;
554
555    cert_builder.append_extension(BasicConstraints::new().build()?)?;
556
557    cert_builder.append_extension(
558        KeyUsage::new()
559            .critical()
560            .digital_signature()
561            .key_encipherment()
562            .build()?,
563    )?;
564
565    cert_builder.append_extension(
566        ExtendedKeyUsage::new()
567            // .critical()
568            .server_auth()
569            .build()?,
570    )?;
571
572    let subject_key_identifier = SubjectKeyIdentifier::new()
573        .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
574    cert_builder.append_extension(subject_key_identifier)?;
575
576    let auth_key_identifier = AuthorityKeyIdentifier::new()
577        .keyid(false)
578        .issuer(false)
579        .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
580    cert_builder.append_extension(auth_key_identifier)?;
581
582    let subject_alt_name = SubjectAlternativeName::new()
583        .dns(domain_name)
584        .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
585    cert_builder.append_extension(subject_alt_name)?;
586
587    cert_builder.sign(&ca_handle.key, get_signing_func())?;
588    let int_cert = cert_builder.build();
589
590    Ok(CertHandle {
591        key: int_key,
592        cert: int_cert,
593        chain: vec![ca_handle.cert.clone()],
594    })
595}
596
597#[test]
598// might as well test my logic
599fn test_enforced_minimums() {
600    let good_ca_configs = vec![
601        // test rsa 4096 (ok)
602        (KeyType::Rsa, 4096, false),
603        // test rsa 2048 (ok)
604        (KeyType::Rsa, 2048, false),
605        // test ec 256 (ok)
606        (KeyType::Ec, 256, false),
607    ];
608    good_ca_configs.into_iter().for_each(|config| {
609        dbg!(&config);
610        assert!(CAConfig::new(config.0, config.1, config.2).is_ok());
611    });
612    let bad_ca_configs = vec![
613        // test rsa 1024 (no)
614        (KeyType::Rsa, 1024, false),
615        // test ec 128 (no)
616        (KeyType::Ec, 128, false),
617    ];
618    bad_ca_configs.into_iter().for_each(|config| {
619        dbg!(&config);
620        assert!(CAConfig::new(config.0, config.1, config.2).is_err());
621    });
622}
623
624#[test]
625fn test_ca_loader() {
626    let ca_key_tempfile = tempfile::NamedTempFile::new().unwrap();
627    let ca_cert_tempfile = tempfile::NamedTempFile::new().unwrap();
628    // let's test the defaults first
629
630    let ca_config = CAConfig::default();
631    if let Ok(ca) = build_ca(Some(ca_config)) {
632        write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
633        assert!(load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path()).is_ok());
634    };
635
636    let good_ca_configs = vec![
637        // test rsa 4096 (ok)
638        (KeyType::Rsa, 4096, false),
639        // test rsa 2048 (ok)
640        (KeyType::Rsa, 2048, false),
641        // test ec 256 (ok)
642        (KeyType::Ec, 256, false),
643    ];
644    good_ca_configs.into_iter().for_each(|config| {
645        println!("testing good config {:?}", config);
646        let ca_config = CAConfig::new(config.0, config.1, config.2).unwrap();
647        let ca = build_ca(Some(ca_config)).unwrap();
648        write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
649        let ca_result = load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path());
650        println!("result: {:?}", ca_result);
651        assert!(ca_result.is_ok());
652    });
653    let bad_ca_configs = vec![
654        // test rsa 1024 (bad)
655        (KeyType::Rsa, 1024, true),
656    ];
657    bad_ca_configs.into_iter().for_each(|config| {
658        println!(
659            "\ntesting bad config keytype: {:?} key size: {}, skip_enforce_minimums: {}",
660            config.0, config.1, config.2
661        );
662        let ca_config = CAConfig::new(config.0, config.1, config.2).unwrap();
663        let ca = build_ca(Some(ca_config)).unwrap();
664        write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
665        let ca_result = load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path());
666        println!("result: {:?}", ca_result);
667        assert!(ca_result.is_err());
668    });
669}