Huakun Shen da8e37c4a1
Security with cryptography (#32)
* Add some experiment code for crypto crate

* feat: add crypto crate with ssl, rsa, ed25519 for https, encryption, signing

* Implement aes encryption helper in crypto crate

* ci: add dep for CI rust test (protobuf)

* fix: try to fix window CI with next

* fix: CI

* ci: add dep for ubuntu

* ci: fix

* fix: openssl lib path in CI

* fix: CI

* update applications-rs, disable some tests

* fix: CI

* feat: add file transfer grpc proto and server setup

* CI: try to fix CI

* fix: missing proto in build.rs

* ci: add cargo build before cargo test

* fix: grpc file descriptor

* ci: fix CI by removing a redundant main.rs

* fix: disable local windows test in applications-rs which fails CI

* ci: run CI rust test only on ubuntu, windows is failing. will be handled in another PR

* fix: vue template

* fix: allow unused variable

* fix: remove node:buffer type from api shell.ts to avoid frontend build error

* try to fix test in create-kunkun

* upgrade api to 0.0.44, remove node:buffer

* upgrade next template to 15

* feat: turn the default server into a https server

* feat: make SSL certificate loadable from env

* feat: add conditional SSL cert

in debug mode, use local default cert, in production generate new self-signed cert every time app starts

* chore: add vscode debug config

* feat: add server public key

* feat: setup sqlite db encryption

* fix: settings hotkey

* chore: add .gitkeep

* ci: add node-fetch to dep for api package
2024-11-24 00:45:36 -05:00

90 lines
3.4 KiB
Rust

use openssl::{
hash::MessageDigest,
pkey::{PKey, Private},
rsa::Rsa,
x509::{extension::SubjectAlternativeName, X509NameBuilder, X509},
};
pub fn generate_self_signed_certificate(
rsa: &Rsa<Private>,
days: u32,
) -> anyhow::Result<(Vec<u8>, Vec<u8>)> {
let private_key = PKey::from_rsa(rsa.to_owned())?;
let mut x509_name_builder = X509NameBuilder::new()?;
x509_name_builder.append_entry_by_text("C", "US")?; // Country
x509_name_builder.append_entry_by_text("ST", "Localhost")?; // State/Province
x509_name_builder.append_entry_by_text("L", "Localhost")?; // Locality
x509_name_builder.append_entry_by_text("O", "Localhost Development")?; // Organization
x509_name_builder.append_entry_by_text("CN", "localhost")?; // Common Name
let x509_name = x509_name_builder.build();
let mut builder = X509::builder()?;
builder.set_version(2)?; // X.509 v3
builder.set_subject_name(&x509_name)?;
builder.set_issuer_name(&x509_name)?; // Self-signed
builder.set_pubkey(&private_key)?;
// Set certificate validity
let not_before = openssl::asn1::Asn1Time::days_from_now(0)?; // Start now
let not_after = openssl::asn1::Asn1Time::days_from_now(days)?; // Valid for 1 year
builder.set_not_before(&not_before)?;
builder.set_not_after(&not_after)?;
// Add Subject Alternative Name (SAN) for localhost
let subject_alt_name = SubjectAlternativeName::new()
.dns("localhost") // Ensures the certificate is valid for "localhost"
.build(&builder.x509v3_context(None, None))?;
builder.append_extension(subject_alt_name)?;
builder.sign(&private_key, MessageDigest::sha256())?;
let x509 = builder.build();
let private_key_pem = private_key.private_key_to_pem_pkcs8()?;
let certificate_pem = x509.to_pem()?;
Ok((private_key_pem, certificate_pem))
}
#[cfg(test)]
mod tests {
use axum::{routing::get, Router};
use axum_server::tls_rustls::RustlsConfig;
use reqwest;
use std::{net::SocketAddr, time::Duration};
use super::*;
#[tokio::test]
async fn test_generate_self_signed_certificate() -> anyhow::Result<()> {
let rsa = Rsa::generate(2048)?;
let (private_key_pem, certificate_pem) = generate_self_signed_certificate(&rsa, 365)?;
let config = RustlsConfig::from_pem(certificate_pem, private_key_pem)
.await
.unwrap();
async fn handler() -> &'static str {
"Hello, World!"
}
let handle = axum_server::Handle::new();
let app = Router::new().route("/", get(handler));
// run https server
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
let server = axum_server::bind_rustls(addr, config)
.handle(handle.clone())
.serve(app.into_make_service());
let server_handle = tokio::spawn(server);
// send a request to server, trust the certificate
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()?;
let response = client.get("https://localhost:8080").send().await?;
assert_eq!(response.status().is_success(), true);
// read the response body
let body = response.text().await?;
assert_eq!(body, "Hello, World!");
println!("shutting down server");
handle.graceful_shutdown(Some(Duration::from_secs(10)));
println!("server shutdown");
server_handle.abort();
Ok(())
}
}