ca: Add support for encrypted private keys

master
Dustin 2023-11-04 15:39:25 -05:00
parent ac9681e0c3
commit 3b42be1797
5 changed files with 193 additions and 7 deletions

161
Cargo.lock generated
View File

@ -17,6 +17,41 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "aes"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "aes-gcm"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "aho-corasick"
version = "1.1.2"
@ -176,6 +211,17 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bcrypt-pbkdf"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2"
dependencies = [
"blowfish",
"pbkdf2",
"sha2",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@ -206,6 +252,25 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]]
name = "blowfish"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7"
dependencies = [
"byteorder",
"cipher",
]
[[package]]
name = "bumpalo"
version = "3.14.0"
@ -224,6 +289,15 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
]
[[package]]
name = "cc"
version = "1.0.83"
@ -239,6 +313,17 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chacha20"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "cipher"
version = "0.4.4"
@ -286,6 +371,15 @@ dependencies = [
"typenum",
]
[[package]]
name = "ctr"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
"cipher",
]
[[package]]
name = "curve25519-dalek"
version = "4.1.1"
@ -593,6 +687,16 @@ dependencies = [
"wasi",
]
[[package]]
name = "ghash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
dependencies = [
"opaque-debug",
"polyval",
]
[[package]]
name = "gimli"
version = "0.28.0"
@ -722,6 +826,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"block-padding",
"generic-array",
]
@ -944,6 +1049,12 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "option-ext"
version = "0.2.0"
@ -1014,6 +1125,15 @@ dependencies = [
"subtle",
]
[[package]]
name = "pbkdf2"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
"digest",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
@ -1094,6 +1214,29 @@ version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0"
[[package]]
name = "poly1305"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "polyval"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb"
dependencies = [
"cfg-if",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@ -1533,8 +1676,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f"
dependencies = [
"aes",
"aes-gcm",
"cbc",
"chacha20",
"cipher",
"ctr",
"poly1305",
"ssh-encoding",
"subtle",
]
[[package]]
@ -1554,6 +1704,7 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2180b3bc4955efd5661a97658d3cf4c8107e0d132f619195afe9486c13cca313"
dependencies = [
"bcrypt-pbkdf",
"ed25519-dalek",
"p256",
"p384",
@ -1831,6 +1982,16 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"crypto-common",
"subtle",
]
[[package]]
name = "untrusted"
version = "0.7.1"

View File

@ -12,7 +12,7 @@ jsonwebtoken = { version = "8.3.0", default-features = false }
rand_core = { version = "0.6.4", features = ["getrandom"] }
serde = { version = "1.0.190", features = ["derive"] }
serde_json = "1.0.108"
ssh-key = { version = "0.6.2", features = ["serde", "ed25519", "getrandom"] }
ssh-key = { version = "0.6.2", features = ["serde", "ed25519", "getrandom", "encryption"] }
tokio = { version = "1.33.0", features = ["rt", "macros", "net", "signal", "fs", "io-util"] }
toml = "0.8.6"
tracing = { version = "0.1.40", features = ["log"] }

View File

@ -7,7 +7,7 @@ use ssh_key::rand_core::OsRng;
use ssh_key::{Certificate, PrivateKey, PublicKey};
use tokio::fs::File;
use tokio::io::AsyncReadExt;
use tracing::debug;
use tracing::{debug, warn};
#[derive(Debug)]
pub enum CertError {
@ -91,7 +91,10 @@ impl std::error::Error for LoadKeyError {
}
/// Load an SSH private key from a file
pub async fn load_private_key<P>(path: P) -> Result<PrivateKey, LoadKeyError>
pub async fn load_private_key<P>(
path: P,
passphrase_path: Option<P>,
) -> Result<PrivateKey, LoadKeyError>
where
P: AsRef<Path>,
{
@ -99,7 +102,24 @@ where
debug!("Loading private key from {}", path.as_ref().display());
let mut f = File::open(path).await?;
f.read_to_end(&mut data).await?;
parse_private_key(&data)
let privkey = parse_private_key(&data)?;
if privkey.is_encrypted() {
if let Some(path) = passphrase_path {
debug!(
"Loading private key passphrase from {}",
path.as_ref().display()
);
let mut passphrase = Vec::new();
let mut f = File::open(path).await?;
f.read_to_end(&mut passphrase).await?;
Ok(privkey.decrypt(passphrase)?)
} else {
warn!("Private key is encrypted but no passphrase provided");
Ok(privkey)
}
} else {
Ok(privkey)
}
}
/// Parse an SSH private key from a slice of bytes

View File

@ -48,6 +48,7 @@ pub struct HostCaConfig {
/// Path to the Host CA private key file
#[serde(default = "default_host_ca_key")]
pub private_key_file: PathBuf,
pub private_key_passphrase_file: Option<PathBuf>,
/// Duration of issued host certificates
#[serde(default = "default_host_cert_duration")]
@ -58,6 +59,7 @@ impl Default for HostCaConfig {
fn default() -> Self {
Self {
private_key_file: default_host_ca_key(),
private_key_passphrase_file: None,
cert_duration: default_host_cert_duration(),
}
}

View File

@ -115,9 +115,12 @@ pub(super) async fn sign_host_cert(
let config = &ctx.config;
let duration = Duration::from_secs(config.ca.host.cert_duration);
let privkey = ca::load_private_key(&config.ca.host.private_key_file)
.await
.map_err(SignKeyError::LoadPrivateKey)?;
let privkey = ca::load_private_key(
&config.ca.host.private_key_file,
config.ca.host.private_key_passphrase_file.as_ref(),
)
.await
.map_err(SignKeyError::LoadPrivateKey)?;
let pubkey = ca::parse_public_key(&body.pubkey)
.map_err(SignKeyError::ParsePublicKey)?;