Skip to content

RUST-226 Support tlsCertificateKeyFilePassword #1256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
openssl support
  • Loading branch information
abr-egn committed Dec 2, 2024
commit e79400286d414d1c34969c909688666b99ec6027
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ exclude = [
default = ["compat-3-0-0", "rustls-tls", "dns-resolver"]
compat-3-0-0 = []
sync = []
rustls-tls = ["dep:rustls", "dep:rustls-pemfile", "dep:tokio-rustls", "dep:pkcs8", "dep:pem"]
rustls-tls = ["dep:rustls", "dep:rustls-pemfile", "dep:tokio-rustls"]
openssl-tls = ["dep:openssl", "dep:openssl-probe", "dep:tokio-openssl"]
dns-resolver = ["dep:hickory-resolver", "dep:hickory-proto"]

Expand Down Expand Up @@ -95,9 +95,9 @@ mongodb-internal-macros = { path = "macros", version = "3.1.0" }
num_cpus = { version = "1.13.1", optional = true }
openssl = { version = "0.10.38", optional = true }
openssl-probe = { version = "0.1.5", optional = true }
pem = { version = "3.0.4", optional = true }
pem = { version = "3.0.4" }
percent-encoding = "2.0.0"
pkcs8 = { version = "0.10.2", features = ["encryption", "pkcs5"], optional = true }
pkcs8 = { version = "0.10.2", features = ["encryption", "pkcs5"] }
rand = { version = "0.8.3", features = ["small_rng"] }
rayon = { version = "1.5.3", optional = true }
rustc_version_runtime = "0.3.0"
Expand Down
1 change: 1 addition & 0 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod acknowledged_message;
))]
mod http;
mod join_handle;
mod pem;
#[cfg(any(feature = "in-use-encryption", test))]
pub(crate) mod process;
#[cfg(feature = "dns-resolver")]
Expand Down
30 changes: 30 additions & 0 deletions src/runtime/pem.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use crate::error::{ErrorKind, Result};

pub(crate) fn decrypt_private_key(pem_data: &[u8], password: &[u8]) -> Result<Vec<u8>> {
let pems = pem::parse_many(&pem_data).map_err(|error| ErrorKind::InvalidTlsConfig {
message: format!("Could not parse pemfile: {}", error),
})?;
let mut iter = pems
.into_iter()
.filter(|pem| pem.tag() == "ENCRYPTED PRIVATE KEY");
let encrypted_bytes = match iter.next() {
Some(pem) => pem.into_contents(),
None => {
return Err(ErrorKind::InvalidTlsConfig {
message: "No encrypted private keys found".into(),
}
.into())
}
};
let encrypted_key = pkcs8::EncryptedPrivateKeyInfo::try_from(encrypted_bytes.as_slice())
.map_err(|error| ErrorKind::InvalidTlsConfig {
message: format!("Invalid encrypted private key: {}", error),
})?;
let decrypted_key =
encrypted_key
.decrypt(password)
.map_err(|error| ErrorKind::InvalidTlsConfig {
message: format!("Failed to decrypt private key: {}", error),
})?;
Ok(decrypted_key.as_bytes().to_vec())
}
37 changes: 26 additions & 11 deletions src/runtime/tls_openssl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use crate::{
error::{Error, ErrorKind, Result},
};

use super::pem::decrypt_private_key;

pub(super) type TlsStream = SslStream<TcpStream>;

/// Configuration required to use TLS. Creating this is expensive, so its best to cache this value
Expand All @@ -38,11 +40,7 @@ impl TlsConfig {
None => true,
};

let connector = make_openssl_connector(options).map_err(|e| {
Error::from(ErrorKind::InvalidTlsConfig {
message: e.to_string(),
})
})?;
let connector = make_openssl_connector(options)?;

Ok(TlsConfig {
connector,
Expand Down Expand Up @@ -73,26 +71,43 @@ pub(super) async fn tls_connect(
Ok(stream)
}

fn make_openssl_connector(cfg: TlsOptions) -> std::result::Result<SslConnector, ErrorStack> {
let mut builder = SslConnector::builder(SslMethod::tls_client())?;
fn make_openssl_connector(cfg: TlsOptions) -> Result<SslConnector> {
let openssl_err = |e: ErrorStack| {
Error::from(ErrorKind::InvalidTlsConfig {
message: e.to_string(),
})
};

let mut builder = SslConnector::builder(SslMethod::tls_client()).map_err(openssl_err)?;

let TlsOptions {
allow_invalid_certificates,
ca_file_path,
cert_key_file_path,
allow_invalid_hostnames: _,
tls_certificate_key_file_password: _,
tls_certificate_key_file_password,
} = cfg;

if let Some(true) = allow_invalid_certificates {
builder.set_verify(SslVerifyMode::NONE);
}
if let Some(path) = ca_file_path {
builder.set_ca_file(path)?;
builder.set_ca_file(path).map_err(openssl_err)?;
}
if let Some(path) = cert_key_file_path {
builder.set_certificate_file(path.clone(), SslFiletype::PEM)?;
builder.set_private_key_file(path, SslFiletype::PEM)?;
builder
.set_certificate_file(path.clone(), SslFiletype::PEM)
.map_err(openssl_err)?;
if let Some(key_pw) = tls_certificate_key_file_password {
let contents = std::fs::read(&path)?;
let key_bytes = decrypt_private_key(&contents, &key_pw)?;
let key = openssl::pkey::PKey::private_key_from_pem(&key_bytes).map_err(openssl_err)?;
builder.set_private_key(&key).map_err(openssl_err)?;
} else {
builder
.set_private_key_file(path, SslFiletype::PEM)
.map_err(openssl_err)?;
}
}

Ok(builder.build())
Expand Down
30 changes: 3 additions & 27 deletions src/runtime/tls_rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ use crate::{
error::{ErrorKind, Result},
};

use super::pem::decrypt_private_key;

pub(super) type TlsStream = tokio_rustls::client::TlsStream<TcpStream>;

/// Configuration required to use TLS. Creating this is expensive, so its best to cache this value
Expand Down Expand Up @@ -106,33 +108,7 @@ fn make_rustls_config(cfg: TlsOptions) -> Result<rustls::ClientConfig> {
let key = if let Some(key_pw) = cfg.tls_certificate_key_file_password.as_deref() {
let mut contents = vec![];
file.read_to_end(&mut contents)?;
let pems = pem::parse_many(&contents).map_err(|error| ErrorKind::InvalidTlsConfig {
message: format!("Could not parse {}: {}", path.display(), error),
})?;
let mut iter = pems
.into_iter()
.filter(|pem| pem.tag() == "ENCRYPTED PRIVATE KEY");
match iter.next() {
Some(pem) => {
let encrypted = pkcs8::EncryptedPrivateKeyInfo::try_from(pem.contents())
.map_err(|error| ErrorKind::InvalidTlsConfig {
message: format!("Invalid encrypted client certificate: {}", error),
})?;
let decrypted =
encrypted
.decrypt(key_pw)
.map_err(|error| ErrorKind::InvalidTlsConfig {
message: format!("Failed to decrypt client certificate: {}", error),
})?;
rustls::PrivateKey(decrypted.as_bytes().to_vec())
}
None => {
return Err(ErrorKind::InvalidTlsConfig {
message: format!("No PEM-encoded keys in {}", path.display()),
}
.into())
}
}
rustls::PrivateKey(decrypt_private_key(&contents, key_pw)?)
} else {
loop {
match read_one(&mut file) {
Expand Down