certs: refactor certificate logic into module

This commit is contained in:
2025-07-04 22:13:38 -06:00
parent 8857e226fe
commit b4e22e09b8
3 changed files with 115 additions and 88 deletions
+112
View File
@@ -0,0 +1,112 @@
use std::fs;
use std::io::Write;
use anyhow::Result;
use openssl::hash::MessageDigest;
use openssl::pkey::{PKey, Private};
use openssl::rsa::Rsa;
use openssl::x509::X509;
pub fn get_cert_and_key() -> Result<(X509, PKey<Private>)> {
if let Ok((cert, key)) = load_cert_and_key_from_disk() {
Ok((cert, key))
} else {
generate_cert_and_key()
}
}
pub fn load_cert_and_key_from_disk() -> Result<(X509, PKey<Private>)> {
let project_dirs =
directories::ProjectDirs::from("xyz", "ohea", "gamestream-webtransport-proxy")
.ok_or(anyhow::anyhow!("Could not get project dirs"))?;
let data_dir = project_dirs.data_dir();
let cert_dir = data_dir.join("certs");
fs::create_dir_all(&cert_dir)?;
let cert_filepath = cert_dir.join("cert");
let key_filepath = cert_dir.join("key");
let cert_bytes = fs::read(cert_filepath)?;
let key_bytes = fs::read(key_filepath)?;
let cert = X509::from_pem(&cert_bytes)?;
let key = PKey::<Private>::private_key_from_pem(&key_bytes)?;
Ok((cert, key))
}
pub fn generate_cert_and_key() -> Result<(X509, PKey<Private>)> {
let rsa = Rsa::generate(2048)?;
let key = PKey::from_rsa(rsa)?;
let mut cert_builder = X509::builder()?;
let serial_number_u32 = openssl::bn::BigNum::from_u32(0)?;
let serial_number = openssl::asn1::Asn1Integer::from_bn(&serial_number_u32)?;
let now_unix = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("Time went backwards")
.as_secs()
- 1000000;
let now = openssl::asn1::Asn1Time::from_unix(now_unix as i64)?;
let ten_years_from_now = openssl::asn1::Asn1Time::days_from_now(365 * 10)?;
let mut x509_name_builder = openssl::x509::X509NameBuilder::new()?;
x509_name_builder.append_entry_by_text("CN", "NVIDIA GameStream Client")?;
let x509_name = x509_name_builder.build();
cert_builder.set_version(2)?;
cert_builder.set_not_before(&now)?;
cert_builder.set_not_after(&ten_years_from_now)?;
cert_builder.set_pubkey(&key)?;
cert_builder.set_serial_number(&serial_number)?;
cert_builder.set_issuer_name(&x509_name)?;
cert_builder.set_subject_name(&x509_name)?;
cert_builder.sign(&key, MessageDigest::sha256())?;
let cert = cert_builder.build();
save_cert_and_key_to_disk(&cert, &key)?;
Ok((cert, key))
}
pub fn save_cert_and_key_to_disk(cert: &X509, key: &PKey<Private>) -> Result<()> {
let project_dirs =
directories::ProjectDirs::from("xyz", "ohea", "gamestream-webtransport-proxy")
.ok_or(anyhow::anyhow!("Could not get project dirs"))?;
let data_dir = project_dirs.data_dir();
let cert_dir = data_dir.join("certs");
fs::create_dir_all(&cert_dir)?;
let cert_filepath = cert_dir.join("cert");
let key_filepath = cert_dir.join("key");
let mut cert_file_builder = std::fs::OpenOptions::new();
cert_file_builder.create(true);
cert_file_builder.truncate(true);
cert_file_builder.write(true);
let mut key_file_builder = std::fs::OpenOptions::new();
key_file_builder.create(true);
key_file_builder.truncate(true);
key_file_builder.write(true);
#[cfg(target_family = "unix")]
{
use std::os::unix::fs::OpenOptionsExt;
key_file_builder.mode(0o600);
cert_file_builder.mode(0o600);
}
let mut cert_file = cert_file_builder.open(&cert_filepath)?;
let mut key_file = key_file_builder.open(&key_filepath)?;
cert_file.write_all(&cert.to_pem()?)?;
key_file.write_all(&key.private_key_to_pem_pkcs8()?)?;
Ok(())
}
pub fn http_client_with_identity() {}
@@ -7,6 +7,7 @@ use moonlight_common_c_sys::{
STREAM_CFG_LOCAL, VIDEO_FORMAT_H264, STREAM_CFG_LOCAL, VIDEO_FORMAT_H264,
}; };
mod certs;
mod pair; mod pair;
fn get_server_info() -> _SERVER_INFORMATION { fn get_server_info() -> _SERVER_INFORMATION {
+2 -88
View File
@@ -1,14 +1,10 @@
use anyhow::Result; use anyhow::Result;
use openssl::hash::MessageDigest;
use openssl::pkey::{PKey, Private}; use openssl::pkey::{PKey, Private};
use openssl::rsa::Rsa;
use openssl::sha::Sha256; use openssl::sha::Sha256;
use openssl::x509::X509; use openssl::x509::X509;
use rand::Rng; use rand::Rng;
use salvo::prelude::*; use salvo::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fs;
use std::io::Write;
#[derive(Debug, Deserialize, ToSchema)] #[derive(Debug, Deserialize, ToSchema)]
struct PostPairParams { struct PostPairParams {
@@ -55,82 +51,6 @@ struct ServerPairingSecret {
signature: Vec<u8>, signature: Vec<u8>,
} }
fn get_cert_and_private_key() -> Result<(X509, PKey<Private>)> {
let rsa = Rsa::generate(2048)?;
let key_pair = PKey::from_rsa(rsa)?;
let mut cert_builder = X509::builder()?;
let serial_number_u32 = openssl::bn::BigNum::from_u32(0)?;
let serial_number = openssl::asn1::Asn1Integer::from_bn(&serial_number_u32)?;
let now_unix = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("Time went backwards")
.as_secs()
- 1000000;
let now = openssl::asn1::Asn1Time::from_unix(now_unix as i64)?;
let ten_years_from_now = openssl::asn1::Asn1Time::days_from_now(365 * 10)?;
let mut x509_name_builder = openssl::x509::X509NameBuilder::new()?;
x509_name_builder.append_entry_by_text("CN", "NVIDIA GameStream Client")?;
let x509_name = x509_name_builder.build();
cert_builder.set_version(2)?;
cert_builder.set_not_before(&now)?;
cert_builder.set_not_after(&ten_years_from_now)?;
cert_builder.set_pubkey(&key_pair)?;
cert_builder.set_serial_number(&serial_number)?;
cert_builder.set_issuer_name(&x509_name)?;
cert_builder.set_subject_name(&x509_name)?;
cert_builder.sign(&key_pair, MessageDigest::sha256())?;
let cert = cert_builder.build();
Ok((cert, key_pair))
}
fn save_cert_and_key_to_disk(
cert: X509,
key: PKey<Private>,
host: &String,
port: u16,
) -> Result<()> {
let project_dirs =
directories::ProjectDirs::from("xyz", "ohea", "gamestream-webtransport-proxy")
.ok_or(anyhow::anyhow!("Could not get project dirs"))?;
let data_dir = project_dirs.data_dir();
let cert_dir = data_dir.join("certs");
fs::create_dir_all(&cert_dir)?;
let cert_filepath = cert_dir.join(format!("{host}_{port}_cert"));
let key_filepath = cert_dir.join(format!("{host}_{port}_key"));
let mut cert_file_builder = std::fs::OpenOptions::new();
cert_file_builder.create(true);
cert_file_builder.truncate(true);
cert_file_builder.write(true);
let mut key_file_builder = std::fs::OpenOptions::new();
key_file_builder.create(true);
key_file_builder.truncate(true);
key_file_builder.write(true);
#[cfg(target_family = "unix")]
{
use std::os::unix::fs::OpenOptionsExt;
key_file_builder.mode(0o600);
cert_file_builder.mode(0o600);
}
let mut cert_file = cert_file_builder.open(&cert_filepath)?;
let mut key_file = key_file_builder.open(&key_filepath)?;
cert_file.write_all(&cert.to_pem()?)?;
key_file.write_all(&key.private_key_to_pem_pkcs8()?)?;
Ok(())
}
async fn get_url(base_url: &mut url_constructor::UrlConstructor) -> Result<String> { async fn get_url(base_url: &mut url_constructor::UrlConstructor) -> Result<String> {
let mut uuidv2 = [0u8; 16]; let mut uuidv2 = [0u8; 16];
openssl::rand::rand_bytes(&mut uuidv2)?; openssl::rand::rand_bytes(&mut uuidv2)?;
@@ -425,8 +345,8 @@ pub async fn post_pair(body: salvo::oapi::extract::JsonBody<PostPairParams>) ->
return StatusCode::INTERNAL_SERVER_ERROR; return StatusCode::INTERNAL_SERVER_ERROR;
} }
// Generate certificates // Get or generate cert / private key
let (cert, private_key) = match get_cert_and_private_key() { let (cert, private_key) = match crate::certs::get_cert_and_key() {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
println!("Could not generate certs: {e}"); println!("Could not generate certs: {e}");
@@ -496,11 +416,5 @@ pub async fn post_pair(body: salvo::oapi::extract::JsonBody<PostPairParams>) ->
println!("Paired with server {host}:{port} successfully!"); println!("Paired with server {host}:{port} successfully!");
} }
// Save certificate to disk so it can be used for subsequent connections
if let Err(e) = save_cert_and_key_to_disk(cert, private_key, &params.host, params.port) {
println!("Could not save cert and key to disk");
return StatusCode::INTERNAL_SERVER_ERROR;
};
StatusCode::OK StatusCode::OK
} }