Add cert generation.
This commit is contained in:
parent
76d606a58f
commit
ef8690bcd9
|
|
@ -7,5 +7,5 @@ edition = "2024"
|
||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
clap = { version = "4.5.53", features = ["derive"] }
|
clap = { version = "4.5.53", features = ["derive"] }
|
||||||
pem = "3.0.6"
|
pem = "3.0.6"
|
||||||
rcgen = { version = "0.14.6", features = ["fips"] }
|
rcgen = { version = "0.14.6", features = ["fips", "x509-parser"] }
|
||||||
time = "0.3.44"
|
time = "0.3.44"
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,10 @@ fn main() -> Result<()> {
|
||||||
format!("{}/key.pem", &args.output),
|
format!("{}/key.pem", &args.output),
|
||||||
key_pair.serialize_pem().as_bytes(),
|
key_pair.serialize_pem().as_bytes(),
|
||||||
)?;
|
)?;
|
||||||
|
fs::write(
|
||||||
|
format!("{}/public.pem", &args.output),
|
||||||
|
key_pair.public_key_pem().as_bytes(),
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
#![feature(duration_constructors)]
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use rcgen::{
|
||||||
|
CertificateParams, DistinguishedName, DnType, ExtendedKeyUsagePurpose,
|
||||||
|
Issuer, KeyPair, KeyUsagePurpose, SanType,
|
||||||
|
};
|
||||||
|
use rcgen::string::Ia5String;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
|
/// Generate a Certificate Authority with some opinionated
|
||||||
|
/// options selected.
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// Common name for this cert.
|
||||||
|
#[arg(long)]
|
||||||
|
common_name: String,
|
||||||
|
|
||||||
|
/// Email address to assign to cert.
|
||||||
|
#[arg(long)]
|
||||||
|
email_address: Option<String>,
|
||||||
|
|
||||||
|
/// Domain address to assign to cert.
|
||||||
|
#[arg(long)]
|
||||||
|
domain_name: Option<String>,
|
||||||
|
|
||||||
|
/// Set to make this cert valid for client authentication.
|
||||||
|
#[arg(long)]
|
||||||
|
client_auth: bool,
|
||||||
|
|
||||||
|
/// Set to make this cert valid for server authentication.
|
||||||
|
#[arg(long)]
|
||||||
|
server_auth: bool,
|
||||||
|
|
||||||
|
/// Days for CA to valid for.
|
||||||
|
#[arg(long, default_value = "365")]
|
||||||
|
valid_length: i64,
|
||||||
|
|
||||||
|
/// Path to CA folder.
|
||||||
|
#[arg(long)]
|
||||||
|
ca_path: String,
|
||||||
|
|
||||||
|
/// Output directory for artifacts.
|
||||||
|
#[arg(long, short)]
|
||||||
|
output: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
if (args.client_auth && args.server_auth) || (!args.client_auth && !args.server_auth) {
|
||||||
|
return Err(anyhow::Error::msg(
|
||||||
|
"Must set one and only one of client or server auth.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up our identity.
|
||||||
|
let mut params: CertificateParams = Default::default();
|
||||||
|
let earliest_date = OffsetDateTime::now_utc();
|
||||||
|
let latest_date = OffsetDateTime::now_utc()
|
||||||
|
.checked_add(Duration::days(args.valid_length))
|
||||||
|
.ok_or(anyhow::Error::msg("Could not get date."))?;
|
||||||
|
params.not_before = earliest_date;
|
||||||
|
params.not_after = latest_date;
|
||||||
|
params.distinguished_name = DistinguishedName::new();
|
||||||
|
params
|
||||||
|
.distinguished_name
|
||||||
|
.push(DnType::CommonName, args.common_name);
|
||||||
|
if args.email_address.is_some() {
|
||||||
|
let email_address = Ia5String::try_from(args.email_address.unwrap())?;
|
||||||
|
params
|
||||||
|
.subject_alt_names
|
||||||
|
.push(SanType::Rfc822Name(email_address));
|
||||||
|
}
|
||||||
|
if args.domain_name.is_some() {
|
||||||
|
let domain_name = Ia5String::try_from(args.domain_name.unwrap())?;
|
||||||
|
params
|
||||||
|
.subject_alt_names
|
||||||
|
.push(SanType::DnsName(domain_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up our purposes.
|
||||||
|
params.key_usages.push(KeyUsagePurpose::DigitalSignature);
|
||||||
|
if args.client_auth {
|
||||||
|
params
|
||||||
|
.extended_key_usages
|
||||||
|
.push(ExtendedKeyUsagePurpose::ClientAuth);
|
||||||
|
}
|
||||||
|
if args.server_auth {
|
||||||
|
params
|
||||||
|
.extended_key_usages
|
||||||
|
.push(ExtendedKeyUsagePurpose::ServerAuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the ca key pair and cert.
|
||||||
|
let ca_keys_file = PathBuf::from(format!("{}/key.pem", &args.ca_path));
|
||||||
|
let ca_cert_file = PathBuf::from(format!("{}/cert.pem", &args.ca_path));
|
||||||
|
|
||||||
|
let ca_keys_pem = fs::read_to_string(&ca_keys_file)?;
|
||||||
|
let ca_cert_pem = fs::read_to_string(&ca_cert_file)?;
|
||||||
|
|
||||||
|
let ca_key_pair = KeyPair::from_pem(&ca_keys_pem)?;
|
||||||
|
let signer = Issuer::from_ca_cert_pem(&ca_cert_pem, ca_key_pair)?;
|
||||||
|
|
||||||
|
let key_pair = KeyPair::generate()?;
|
||||||
|
let cert = params.signed_by(&key_pair, &signer)?;
|
||||||
|
|
||||||
|
let pem_serialized = cert.pem();
|
||||||
|
|
||||||
|
println!("Saving artifacts.");
|
||||||
|
fs::create_dir_all(&args.output)?;
|
||||||
|
fs::write(
|
||||||
|
format!("{}/cert.pem", &args.output),
|
||||||
|
pem_serialized.as_bytes(),
|
||||||
|
)?;
|
||||||
|
fs::write(
|
||||||
|
format!("{}/key.pem", &args.output),
|
||||||
|
key_pair.serialize_pem().as_bytes(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue