1
0
mirror of https://github.com/pcvolkmer/cert-tools.git synced 2025-04-19 17:06:49 +00:00

feat: graphical cert list

This commit is contained in:
Paul-Christian Volkmer 2025-01-05 21:25:26 +01:00
parent 9847ff3229
commit 3ec36c1795

View File

@ -4,9 +4,11 @@ use cert_tools::{Chain, PrivateKey};
use iced::widget::text_editor::{default, Content, Status}; use iced::widget::text_editor::{default, Content, Status};
use iced::widget::{ use iced::widget::{
button, column, container, horizontal_rule, horizontal_space, row, text, text_editor, button, column, container, horizontal_rule, horizontal_space, row, text, text_editor,
Scrollable, Container, Scrollable,
};
use iced::{
application, clipboard, Background, Border, Color, Element, Font, Length, Shadow, Size, Task,
}; };
use iced::{application, clipboard, Background, Color, Element, Font, Length, Size, Task};
use itertools::Itertools; use itertools::Itertools;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -19,11 +21,18 @@ fn main() -> iced::Result {
.run_with(Ui::new) .run_with(Ui::new)
} }
enum UiMode {
CertList,
Output
}
struct Ui { struct Ui {
cert_file: Option<PathBuf>, cert_file: Option<PathBuf>,
ca_file: Option<PathBuf>, ca_file: Option<PathBuf>,
key_file: Option<PathBuf>, key_file: Option<PathBuf>,
mode: UiMode,
chain: Option<Chain>,
output: Content, output: Content,
status: String, status: String,
} }
@ -35,6 +44,8 @@ impl Ui {
cert_file: None, cert_file: None,
ca_file: None, ca_file: None,
key_file: None, key_file: None,
mode: UiMode::Output,
chain: None,
output: Content::default(), output: Content::default(),
status: String::new(), status: String::new(),
}, },
@ -47,17 +58,20 @@ impl Ui {
} }
fn update(&mut self, message: Message) -> Task<Message> { fn update(&mut self, message: Message) -> Task<Message> {
self.mode = UiMode::Output;
match message { match message {
Message::PickCertFile => Task::perform(pick_file(), Message::SetCertFile), Message::PickCertFile => Task::perform(pick_file(), Message::SetCertFile),
Message::PickCaFile => Task::perform(pick_file(), Message::SetCaFile), Message::PickCaFile => Task::perform(pick_file(), Message::SetCaFile),
Message::PickKeyFile => Task::perform(pick_file(), Message::SetKeyFile), Message::PickKeyFile => Task::perform(pick_file(), Message::SetKeyFile),
Message::ClearCertFile => { Message::ClearCertFile => {
self.cert_file = None; self.cert_file = None;
self.chain = None;
self.output = Content::default(); self.output = Content::default();
Task::none() Task::none()
} }
Message::ClearCaFile => { Message::ClearCaFile => {
self.ca_file = None; self.ca_file = None;
self.chain = None;
self.output = Content::default(); self.output = Content::default();
Task::none() Task::none()
} }
@ -70,8 +84,13 @@ impl Ui {
match file { match file {
Ok(file) => { Ok(file) => {
self.cert_file = Some(file); self.cert_file = Some(file);
self.chain = match self.load_chain() {
Ok(chain) => Some(chain),
_ => None
};
self.output = Content::default(); self.output = Content::default();
}, self.mode = UiMode::CertList;
}
_ => self.cert_file = None, _ => self.cert_file = None,
}; };
Task::none() Task::none()
@ -80,8 +99,13 @@ impl Ui {
match file { match file {
Ok(file) => { Ok(file) => {
self.ca_file = Some(file); self.ca_file = Some(file);
self.chain = match self.load_chain() {
Ok(chain) => Some(chain),
_ => None
};
self.output = Content::default(); self.output = Content::default();
}, self.mode = UiMode::CertList;
}
_ => self.ca_file = None, _ => self.ca_file = None,
}; };
Task::none() Task::none()
@ -91,7 +115,8 @@ impl Ui {
Ok(file) => { Ok(file) => {
self.key_file = Some(file); self.key_file = Some(file);
self.output = Content::default(); self.output = Content::default();
}, self.mode = UiMode::CertList;
}
_ => self.key_file = None, _ => self.key_file = None,
}; };
Task::none() Task::none()
@ -101,6 +126,7 @@ impl Ui {
Ok(output) => { Ok(output) => {
self.output = Content::with_text(output.as_str()); self.output = Content::with_text(output.as_str());
self.status = String::new(); self.status = String::new();
self.mode = UiMode::CertList;
} }
Err(err) => { Err(err) => {
self.output = Content::default(); self.output = Content::default();
@ -122,7 +148,7 @@ impl Ui {
}; };
Task::none() Task::none()
} }
Message::Copy => clipboard::write::<Message>(self.output.text()), Message::Copy => clipboard::write::<Message>(self.output.text())
} }
} }
@ -252,11 +278,60 @@ impl Ui {
value: Color::WHITE, value: Color::WHITE,
..default(theme, Status::Disabled) ..default(theme, Status::Disabled)
}) })
.font(Font::MONOSPACE) .font(Font::MONOSPACE),
) )
.height(Length::Fill) .height(Length::Fill)
}; };
let certs = {
let mut result = column![];
if let Some(chain) = &self.chain {
for cert in chain.certs() {
result =
result.push(
Container::new(column![
text(cert.name().to_string()).size(18),
row![
text("Name: ").width(200),
text(cert.name().to_string())
],
row![
text("Issuer: ").width(200),
text(cert.issuer().to_string())
],
row![
text("SHA-1-Fingerprint: ").width(200),
text(cert.fingerprint().sha1.to_string())
],
row![
text("SHA-256-Fingerprint: ").width(200),
text(cert.fingerprint().sha256.to_string())
],
row![
text("Subject-Key-Id: ").width(200),
text(cert.subject_key_id().to_string())
],
row![
text("Authority_Key-Id: ").width(200),
text(cert.authority_key_id().to_string())
],
])
.padding(4)
.style(|t| container::Style {
border: Border::default().width(1),
background: Some(Background::Color(Color::parse("#eee").unwrap())),
..container::Style::default()
})
.width(Length::Fill),
)
}
};
let content = result.spacing(2);
Scrollable::new(content).height(Length::Fill)
};
let indicator = { let indicator = {
let content = match self.indicator_state() { let content = match self.indicator_state() {
IndicatorState::Unknown => ("?", "#aaaaaa", "#ffffff"), IndicatorState::Unknown => ("?", "#aaaaaa", "#ffffff"),
@ -286,7 +361,10 @@ impl Ui {
.spacing(96), .spacing(96),
horizontal_rule(1), horizontal_rule(1),
buttons, buttons,
output, match self.mode {
UiMode::CertList => certs,
UiMode::Output => output
},
horizontal_rule(1), horizontal_rule(1),
text(&self.status) text(&self.status)
] ]
@ -419,6 +497,28 @@ Authority-Key-Id: {}
} }
Ok(result) Ok(result)
} }
fn load_chain(&self) -> Result<Chain, String> {
if let Some(cert_file) = &self.cert_file {
let chain = Chain::read(cert_file);
if let Ok(mut chain) = chain {
if let Some(ca_file) = &self.ca_file {
if let Ok(ca_chain) = Chain::read(ca_file) {
for ca_cert in ca_chain.into_vec() {
chain.push(ca_cert);
}
} else {
return Err("Cannot read CA file".to_string());
}
}
return Ok(chain);
} else {
return Err("Cannot read Certificate file".to_string());
}
}
Ok(Chain::from(vec![]))
}
fn indicator_state(&self) -> IndicatorState { fn indicator_state(&self) -> IndicatorState {
let mut result = IndicatorState::Unknown; let mut result = IndicatorState::Unknown;
@ -482,7 +582,7 @@ enum Message {
SetKeyFile(Result<PathBuf, Error>), SetKeyFile(Result<PathBuf, Error>),
Print, Print,
Merge, Merge,
Copy, Copy
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]