diff --git a/Cargo.lock b/Cargo.lock index 3a5195194..e2904fc24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2367,9 +2367,11 @@ dependencies = [ "ironrdp-rdpsnd", "ironrdp-svc", "ironrdp-tokio", + "rustls-pemfile", "tokio", "tokio-rustls", "tracing", + "x509-cert", ] [[package]] diff --git a/crates/ironrdp-server/Cargo.toml b/crates/ironrdp-server/Cargo.toml index 38b6d1d64..b13766fae 100644 --- a/crates/ironrdp-server/Cargo.toml +++ b/crates/ironrdp-server/Cargo.toml @@ -15,6 +15,9 @@ categories.workspace = true doctest = true test = false +[features] +helper = ["dep:x509-cert", "dep:rustls-pemfile"] + [dependencies] anyhow = "1.0" tokio = { version = "1", features = ["net", "macros", "sync", "rt"] } @@ -33,6 +36,8 @@ ironrdp-acceptor.workspace = true ironrdp-graphics.workspace = true ironrdp-rdpsnd.workspace = true tracing.workspace = true +x509-cert = { version = "0.2.5", optional = true } +rustls-pemfile = { version = "2.2.0", optional = true } [dev-dependencies] tokio = { version = "1", features = ["sync"] } diff --git a/crates/ironrdp-server/src/helper.rs b/crates/ironrdp-server/src/helper.rs new file mode 100644 index 000000000..cd3673b6a --- /dev/null +++ b/crates/ironrdp-server/src/helper.rs @@ -0,0 +1,54 @@ +use std::{fs::File, io::BufReader, path::Path, sync::Arc}; + +use anyhow::Context; +use rustls_pemfile::{certs, pkcs8_private_keys}; +use tokio_rustls::{rustls, TlsAcceptor}; + +pub struct TlsIdentityCtx { + pub cert: rustls::pki_types::CertificateDer<'static>, + pub priv_key: rustls::pki_types::PrivateKeyDer<'static>, + pub pub_key: Vec, +} + +impl TlsIdentityCtx { + pub fn init_from_paths(cert_path: &Path, key_path: &Path) -> anyhow::Result { + use x509_cert::der::Decode as _; + + let cert = certs(&mut BufReader::new(File::open(cert_path)?)) + .next() + .context("no certificate")??; + + let pub_key = { + let cert = x509_cert::Certificate::from_der(&cert).map_err(std::io::Error::other)?; + cert.tbs_certificate + .subject_public_key_info + .subject_public_key + .as_bytes() + .ok_or_else(|| std::io::Error::other("subject public key BIT STRING is not aligned"))? + .to_owned() + }; + + let priv_key = pkcs8_private_keys(&mut BufReader::new(File::open(key_path)?)) + .next() + .context("no private key")? + .map(rustls::pki_types::PrivateKeyDer::from)?; + + Ok(Self { + cert, + priv_key, + pub_key, + }) + } + + pub fn make_acceptor(&self) -> anyhow::Result { + let mut server_config = rustls::ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(vec![self.cert.clone()], self.priv_key.clone_key()) + .context("bad certificate/key")?; + + // This adds support for the SSLKEYLOGFILE env variable (https://wiki.wireshark.org/TLS#using-the-pre-master-secret) + server_config.key_log = Arc::new(rustls::KeyLogFile::new()); + + Ok(TlsAcceptor::from(Arc::new(server_config))) + } +} diff --git a/crates/ironrdp-server/src/lib.rs b/crates/ironrdp-server/src/lib.rs index 927470216..b48685e7c 100644 --- a/crates/ironrdp-server/src/lib.rs +++ b/crates/ironrdp-server/src/lib.rs @@ -16,11 +16,15 @@ mod clipboard; mod display; mod encoder; mod handler; +#[cfg(feature = "helper")] +mod helper; mod server; mod sound; pub use clipboard::*; pub use display::*; pub use handler::*; +#[cfg(feature = "helper")] +pub use helper::*; pub use server::*; pub use sound::*;