pub struct CertificateManager {
app_id: String,
ca_cert: Option<Vec<u8>>,
ca_key: Option<Vec<u8>>,
server_certs: Arc<RwLock<HashMap<String, ServerCertData>>>,
}Expand description
Main certificate manager for TLS infrastructure
Manages a root CA certificate and generates server certificates as needed. The CA certificate is persisted in the OS keyring for security.
Fields§
§app_id: StringApplication identifier for keyring storage
ca_cert: Option<Vec<u8>>CA certificate PEM (cached from keyring)
ca_key: Option<Vec<u8>>CA private key PEM (cached from keyring)
server_certs: Arc<RwLock<HashMap<String, ServerCertData>>>Cached server certificates (hostname -> cert data)
Implementations§
Source§impl CertificateManager
impl CertificateManager
Sourceconst KEYRING_SERVICE: &'static str = "CodeEditorLand-TLS"
const KEYRING_SERVICE: &'static str = "CodeEditorLand-TLS"
Keyring service name for certificate storage
Sourceconst KEYRING_CA_CERT: &'static str = "ca_certificate"
const KEYRING_CA_CERT: &'static str = "ca_certificate"
Keyring entry name for CA certificate
Sourceconst KEYRING_CA_KEY: &'static str = "ca_private_key"
const KEYRING_CA_KEY: &'static str = "ca_private_key"
Keyring entry name for CA private key
Sourceconst CA_VALIDITY_DAYS: i64
const CA_VALIDITY_DAYS: i64
Certificate validity period for CA (10 years)
Sourceconst SERVER_VALIDITY_DAYS: i64 = 365
const SERVER_VALIDITY_DAYS: i64 = 365
Certificate validity period for server certs (1 year)
Sourcepub const RENEWAL_THRESHOLD_DAYS: i64 = 30
pub const RENEWAL_THRESHOLD_DAYS: i64 = 30
Renewal threshold (renew if expiring within 30 days)
Sourcepub async fn initialize_ca(&mut self) -> Result<()>
pub async fn initialize_ca(&mut self) -> Result<()>
Initialize or load the CA certificate
This method attempts to load the CA certificate from the keyring. If not found, it generates a new self-signed CA and stores it.
§Example
let mut cert_manager = CertificateManager::new("myapp").await?;
cert_manager.initialize_ca().await?;Sourcefn generate_ca_cert(&self) -> Result<(Vec<u8>, Vec<u8>)>
fn generate_ca_cert(&self) -> Result<(Vec<u8>, Vec<u8>)>
Generate a new self-signed CA certificate
Returns (certificate PEM, private key PEM) tuple.
The CA certificate:
- Uses ECDSA P-256 curve for consistency with DNSSEC
- Has CA:TRUE basic constraint
- Allows keyCertSign and CRLSign key usage
- Valid for 10 years
- Includes proper extensions for CA functionality
Sourcepub async fn get_server_cert(&self, hostname: &str) -> Result<Arc<ServerConfig>>
pub async fn get_server_cert(&self, hostname: &str) -> Result<Arc<ServerConfig>>
Get or generate a server certificate for a specific hostname
§Arguments
hostname- The hostname (e.g., “code.editor.land”)
§Returns
A rustls ServerConfig ready for HTTPS serving
§Example
let mut cert_manager = CertificateManager::new("myapp").await?;
cert_manager.initialize_ca().await?;
let server_config = cert_manager.get_server_cert("code.editor.land").await?;Sourcefn generate_server_cert(&self, hostname: &str) -> Result<ServerCertData>
fn generate_server_cert(&self, hostname: &str) -> Result<ServerCertData>
Generate a server certificate signed by the CA
The certificate includes:
- Specified hostname as Common Name
- Subject Alternative Names: DNS hostname, 127.0.0.1, ::1
- Valid for 1 year with automatic renewal
- Server authentication EKUs
Sourcefn load_ca_from_keyring(&self) -> Result<Option<(Vec<u8>, Vec<u8>)>>
fn load_ca_from_keyring(&self) -> Result<Option<(Vec<u8>, Vec<u8>)>>
Load CA certificate and key from keyring
Returns Some((cert_pem, key_pem)) if found, None otherwise.
Sourcefn save_ca_to_keyring(&self, cert: &[u8], key: &[u8]) -> Result<()>
fn save_ca_to_keyring(&self, cert: &[u8], key: &[u8]) -> Result<()>
Save CA certificate and key to keyring
Sourcepub fn should_renew(&self, cert_pem: &[u8]) -> bool
pub fn should_renew(&self, cert_pem: &[u8]) -> bool
Check if a certificate should be renewed
Returns true if the certificate is expiring within RENEWAL_THRESHOLD_DAYS.
Sourcepub async fn renew_certificate(&mut self, hostname: &str) -> Result<()>
pub async fn renew_certificate(&mut self, hostname: &str) -> Result<()>
Sourcepub async fn build_server_config(
&self,
hostname: &str,
) -> Result<Arc<ServerConfig>>
pub async fn build_server_config( &self, hostname: &str, ) -> Result<Arc<ServerConfig>>
Build a ServerConfig for a specific hostname
This is a convenience wrapper around get_server_cert().
§Arguments
hostname- The hostname (e.g., “code.editor.land”)
§Example
let mut cert_manager = CertificateManager::new("myapp").await?;
cert_manager.initialize_ca().await?;
let server_config = cert_manager.build_server_config("code.editor.land").await?;Sourcepub fn get_ca_cert_pem(&self) -> Option<Vec<u8>>
pub fn get_ca_cert_pem(&self) -> Option<Vec<u8>>
Get the CA certificate in PEM format
This can be used to install the CA in the system trust store or configure the webview to trust it.
§Returns
CA certificate PEM, or None if CA is not initialized
§Example
let mut cert_manager = CertificateManager::new("myapp").await?;
cert_manager.initialize_ca().await?;
let ca_cert = cert_manager.get_ca_cert_pem().unwrap();
println!("CA Certificate:\n{}", String::from_utf8_lossy(&ca_cert));Sourcepub fn get_server_cert_info(&self, hostname: &str) -> Option<CertificateInfo>
pub fn get_server_cert_info(&self, hostname: &str) -> Option<CertificateInfo>
Get information about a server certificate
§Arguments
hostname- The hostname (e.g., “code.editor.land”)
§Returns
CertificateInfo if the certificate exists
§Example
let mut cert_manager = CertificateManager::new("myapp").await?;
cert_manager.initialize_ca().await?;
cert_manager.get_server_cert("code.editor.land").await?;
let info = cert_manager.get_server_cert_info("code.editor.land").unwrap();
println!("Certificate valid until: {}", info.valid_until);Sourcepub fn get_all_certs(&self) -> HashMap<String, CertificateInfo>
pub fn get_all_certs(&self) -> HashMap<String, CertificateInfo>
Get all cached server certificates
§Returns
A HashMap mapping hostnames to certificate info
§Example
let mut cert_manager = CertificateManager::new("myapp").await?;
cert_manager.initialize_ca().await?;
cert_manager.get_server_cert("code.editor.land").await?;
cert_manager.get_server_cert("api.editor.land").await?;
let all_certs = cert_manager.get_all_certs();
for (hostname, info) in all_certs {
println!("{}: valid until {}", hostname, info.valid_until);
}Sourcefn extract_cert_info(
&self,
cert_der: &[u8],
hostname: &str,
is_ca: bool,
) -> Result<CertificateInfo>
fn extract_cert_info( &self, cert_der: &[u8], hostname: &str, is_ca: bool, ) -> Result<CertificateInfo>
Extract certificate information from DER data
Sourcefn check_cert_validity(&self, cert_pem: &[u8]) -> Result<CertValidityResult>
fn check_cert_validity(&self, cert_pem: &[u8]) -> Result<CertValidityResult>
Check certificate validity and renewal status
Sourcefn parse_not_after(not_after: &ASN1Time) -> Result<DateTime<Utc>>
fn parse_not_after(not_after: &ASN1Time) -> Result<DateTime<Utc>>
Parse X.509 not_after time to chrono DateTime
Sourcefn not_as_unix_timestamp(not_after: &ASN1Time) -> Option<i64>
fn not_as_unix_timestamp(not_after: &ASN1Time) -> Option<i64>
Helper function to convert ASN1Time to Unix timestamp
Auto Trait Implementations§
impl Freeze for CertificateManager
impl !RefUnwindSafe for CertificateManager
impl Send for CertificateManager
impl Sync for CertificateManager
impl Unpin for CertificateManager
impl UnsafeUnpin for CertificateManager
impl !UnwindSafe for CertificateManager
Blanket Implementations§
§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSync for T
impl<T> DowncastSync for T
§impl<T> FutureExt for T
impl<T> FutureExt for T
§fn with_context(self, otel_cx: Context) -> WithContext<Self>
fn with_context(self, otel_cx: Context) -> WithContext<Self>
§fn with_current_context(self) -> WithContext<Self>
fn with_current_context(self) -> WithContext<Self>
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<L> LayerExt<L> for L
impl<L> LayerExt<L> for L
§fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
Layered].