use std::{ collections::HashMap, env::args, fs::{self, File}, io::Write, process::{Command, Stdio}, }; use aws_manager::{self, acm, acmpca}; use rcgen::{DistinguishedName, DnType}; use tokio::time::{sleep, Duration}; /// cargo run --example acmpca --features="acm acmpca random-manager" -- [ORG] [COMMON NAME] /// cargo run --example acmpca --features="acm acmpca random-manager" -- "myorg" "hello.com" /// /// 1. create ROOT CA /// 2. get ROOT CA CSR /// 3. issue new cert from the ROOT CA, with the CSR /// 4. import the newly issued cert to the ROOT CA /// 5. ROOT CA is now "ACTIVE" /// #[tokio::main] async fn main() { // ref. https://github.com/env-logger-rs/env_logger/issues/47 env_logger::init_from_env( env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), ); let shared_config = aws_manager::load_config(Some(String::from("us-west-2")), None, None).await; log::info!("region {:?}", shared_config.region().unwrap()); let acm_manager = acm::Manager::new(&shared_config); let acmpca_manager = acmpca::Manager::new(&shared_config); let name = random_manager::secure_string(10); let mut tags = HashMap::new(); tags.insert("Name".to_string(), name); tags.insert("Kind".to_string(), "aws-manager".to_string()); let org = args().nth(1).expect("no org given"); let common_name = args().nth(2).expect("no common name given"); log::info!("org {org}, common name {common_name}"); // // // // // // // // // println!(); println!(); println!(); println!("STEP 1. creating a root CA for the following CSRs"); let root_ca_arn = acmpca_manager .create_root_ca(&org, &common_name, Some(tags)) .await .unwrap(); // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 2. getting the root CSR to import"); let root_ca_csr_to_import = acmpca_manager.get_ca_csr(&root_ca_arn).await.unwrap(); log::info!("root_ca_csr_to_import:\n\n{root_ca_csr_to_import}\n"); let root_ca_csr_to_import_path = random_manager::tmp_path(10, Some(".csr")).unwrap(); { let mut file = File::create(&root_ca_csr_to_import_path).unwrap(); file.write_all(root_ca_csr_to_import.as_bytes()).unwrap(); } log::info!("wrote CSR to {root_ca_csr_to_import_path}"); // ref. let openssl_args = vec![ "req".to_string(), "-text".to_string(), "-noout".to_string(), "-verify".to_string(), "-in".to_string(), root_ca_csr_to_import_path.to_string(), ]; let openssl_cmd = Command::new("openssl") .stderr(Stdio::inherit()) .stdout(Stdio::inherit()) .args(openssl_args) .spawn() .unwrap(); log::info!("ran openssl req with PID {}", openssl_cmd.id()); let res = openssl_cmd.wait_with_output(); match res { Ok(output) => { println!( "openssl output {} bytes:\n{}\n", output.stdout.len(), String::from_utf8(output.stdout).unwrap() ) } Err(e) => { log::warn!("failed to run openssl {}", e) } } // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 3. issuing the self-signed cert from the root CA and CSR"); let issued_self_signed_cert_arn = acmpca_manager .issue_self_signed_cert_from_root_ca( &root_ca_arn, 365, aws_smithy_types::Blob::new(root_ca_csr_to_import), ) .await .unwrap(); // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 4. fetching the newly issued the self-signed cert PEM"); let issued_self_signed_cert_pem = acmpca_manager .get_cert_pem(&root_ca_arn, &issued_self_signed_cert_arn) .await .unwrap(); log::info!("issued_self_signed_cert_pem:\n\n{issued_self_signed_cert_pem}\n"); sleep(Duration::from_secs(3)).await; match acmpca_manager.get_ca_cert_pem(&root_ca_arn).await { Ok(_) => {} Err(e) => { log::warn!("CA cert not imported yet: {:?}", e); } } // ref. let issued_self_signed_cert_pem_path = random_manager::tmp_path(10, Some(".csr")).unwrap(); { let mut file = File::create(&issued_self_signed_cert_pem_path).unwrap(); file.write_all(issued_self_signed_cert_pem.as_bytes()) .unwrap(); } log::info!("wrote issued cert PEM to {issued_self_signed_cert_pem_path}"); // ref. let openssl_args = vec![ "x509".to_string(), "-text".to_string(), "-noout".to_string(), "-in".to_string(), issued_self_signed_cert_pem_path.to_string(), ]; let openssl_cmd = Command::new("openssl") .stderr(Stdio::inherit()) .stdout(Stdio::inherit()) .args(openssl_args) .spawn() .unwrap(); log::info!("ran openssl x509 with PID {}", openssl_cmd.id()); let res = openssl_cmd.wait_with_output(); match res { Ok(output) => { println!( "openssl output {} bytes:\n{}\n", output.stdout.len(), String::from_utf8(output.stdout).unwrap() ) } Err(e) => { log::warn!("failed to run openssl {}", e) } } // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 5. importing the self-signed cert PEM to the root CA"); acmpca_manager .import_cert(&issued_self_signed_cert_pem_path, &root_ca_arn) .await .unwrap(); // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 6. making sure the root CA is now ACTIVE"); sleep(Duration::from_secs(3)).await; let described = acmpca_manager.describe_ca(&root_ca_arn).await.unwrap(); log::info!("described CA: {:?}", described); assert_eq!(described.status().unwrap().as_str(), "ACTIVE"); // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 7. getting the root CA PEM"); let root_ca_cert = acmpca_manager.get_ca_cert_pem(&root_ca_arn).await.unwrap(); log::info!("Root CA cert:\n\n{root_ca_cert}\n"); // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 8. requesting a private certificate"); let requested_private_cert_arn = acm_manager .request_private_cert(&common_name, &root_ca_arn) .await .unwrap(); // ONLY WORKS IF YOU CAN PASS THE VALIDATION (e.g., DNS, email) // ref. if 1 == 10 { // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 9. exporting the private certificate"); let exported_private_cert = acm_manager .export_private_cert( &requested_private_cert_arn, aws_smithy_types::Blob::new("1234"), ) .await .unwrap(); let request_private_cert_key_path = random_manager::tmp_path(10, Some(".key")).unwrap(); { let mut file = File::create(&request_private_cert_key_path).unwrap(); file.write_all( exported_private_cert .private_key() .clone() .unwrap() .as_bytes(), ) .unwrap(); } log::info!("wrote requested private cert key file {request_private_cert_key_path}"); let request_private_cert_cert_path = random_manager::tmp_path(10, Some(".cert")).unwrap(); { let mut file = File::create(&request_private_cert_cert_path).unwrap(); file.write_all( exported_private_cert .certificate() .clone() .unwrap() .as_bytes(), ) .unwrap(); } log::info!("wrote requested private cert cert file {request_private_cert_cert_path}"); fs::remove_file(request_private_cert_key_path).unwrap(); fs::remove_file(request_private_cert_cert_path).unwrap(); } // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 10. writing another CSR"); let mut csr_params = cert_manager::x509::default_params(None, Some(common_name.clone()), false).unwrap(); csr_params.distinguished_name = DistinguishedName::new(); csr_params .distinguished_name .push(DnType::CountryName, "US"); csr_params .distinguished_name .push(DnType::StateOrProvinceName, "NY"); csr_params .distinguished_name .push(DnType::OrganizationName, org); csr_params .distinguished_name .push(DnType::CommonName, common_name.clone()); let csr_entity_to_import = cert_manager::x509::CsrEntity::new_with_parameters(csr_params).unwrap(); let csr_to_import = csr_entity_to_import.csr_pem.clone(); log::info!( "csr_entity_to_import:\n\n{}\n", csr_entity_to_import.csr_pem ); let (csr_key_path, csr_cert_path, csr_to_import_path) = csr_entity_to_import.save(true, None, None, None).unwrap(); log::info!("csr_key_path: {csr_key_path}"); log::info!("csr_cert_path: {csr_cert_path}"); log::info!("csr_to_import_path: {csr_to_import_path}"); // ref. let openssl_args = vec![ "req".to_string(), "-text".to_string(), "-noout".to_string(), "-verify".to_string(), "-in".to_string(), csr_to_import_path.to_string(), ]; let openssl_cmd = Command::new("openssl") .stderr(Stdio::inherit()) .stdout(Stdio::inherit()) .args(openssl_args) .spawn() .unwrap(); log::info!("ran openssl req with PID {}", openssl_cmd.id()); let res = openssl_cmd.wait_with_output(); match res { Ok(output) => { println!( "openssl output {} bytes:\n{}\n", output.stdout.len(), String::from_utf8(output.stdout).unwrap() ) } Err(e) => { log::warn!("failed to run openssl {}", e) } } // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 11. issuing a new end cert from root CA and CSR"); let issued_end_cert_arn = acmpca_manager .issue_end_cert_from_root_ca( &root_ca_arn, 180, aws_smithy_types::Blob::new(csr_to_import), ) .await .unwrap(); // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; sleep(Duration::from_secs(3)).await; println!("STEP 12. fetching the newly issued the end cert PEM"); let issued_end_cert_pem = acmpca_manager .get_cert_pem(&root_ca_arn, &issued_end_cert_arn) .await .unwrap(); log::info!("issued_end_cert_pem:\n\n{issued_end_cert_pem}\n"); // ref. let issued_end_cert_pem_path = random_manager::tmp_path(10, Some(".csr")).unwrap(); { let mut file = File::create(&issued_end_cert_pem_path).unwrap(); file.write_all(issued_end_cert_pem.as_bytes()).unwrap(); } log::info!("wrote issued cert PEM to {issued_end_cert_pem_path}"); // ref. let openssl_args = vec![ "x509".to_string(), "-text".to_string(), "-noout".to_string(), "-in".to_string(), issued_end_cert_pem_path.to_string(), ]; let openssl_cmd = Command::new("openssl") .stderr(Stdio::inherit()) .stdout(Stdio::inherit()) .args(openssl_args) .spawn() .unwrap(); log::info!("ran openssl x509 with PID {}", openssl_cmd.id()); let res = openssl_cmd.wait_with_output(); match res { Ok(output) => { println!( "openssl output {} bytes:\n{}\n", output.stdout.len(), String::from_utf8(output.stdout).unwrap() ) } Err(e) => { log::warn!("failed to run openssl {}", e) } } // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 13. importing the end cert PEM to the root CA and expect failures"); match acmpca_manager .import_cert(&issued_end_cert_pem_path, &root_ca_arn) .await { Ok(_) => {} Err(e) => { log::info!("as expected, it failed {:?}", e) } } sleep(Duration::from_secs(3)).await; let described = acmpca_manager.describe_ca(&root_ca_arn).await.unwrap(); log::info!("described CA: {:?}", described); assert_eq!(described.status().unwrap().as_str(), "ACTIVE"); // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 14. getting the root CA PEM"); sleep(Duration::from_secs(3)).await; let ca_cert = acmpca_manager.get_ca_cert_pem(&root_ca_arn).await.unwrap(); log::info!("CA cert:\n\n{ca_cert}\n"); // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(250)).await; println!("STEP 15. deleting the private certificate"); acm_manager .delete_cert(&requested_private_cert_arn) .await .unwrap(); // // // // // // // // // println!(); println!(); println!(); // The following will fail since the self-signed cert cannot be revoked... sleep(Duration::from_secs(3)).await; println!("STEP 16. disabling root CA"); match acmpca_manager.disable_ca(&root_ca_arn).await { Ok(_) => {} Err(e) => log::warn!("failed disable_ca {:?}", e), }; // // // // // // // // // println!(); println!(); println!(); sleep(Duration::from_secs(3)).await; println!("STEP 16. deleting root CA"); acmpca_manager.delete_ca(&root_ca_arn).await.unwrap(); sleep(Duration::from_secs(3)).await; acmpca_manager.delete_ca(&root_ca_arn).await.unwrap(); // // // // // // // // // println!(); println!(); println!(); log::info!("STEP 17. removing test files"); fs::remove_file(root_ca_csr_to_import_path).unwrap(); fs::remove_file(issued_self_signed_cert_pem_path).unwrap(); fs::remove_file(csr_key_path).unwrap(); fs::remove_file(csr_cert_path).unwrap(); fs::remove_file(csr_to_import_path).unwrap(); }