From 42c0f784e29c191451a0a64aa40a6119d60ac18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= Date: Fri, 5 Feb 2021 16:35:28 +0100 Subject: [PATCH] key: add show-master-pubkey command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and print public key when generating/importing.. Signed-off-by: Fabian Grünbichler --- src/api2/types/mod.rs | 32 +++++++++++ src/bin/proxmox_backup_client/key.rs | 82 ++++++++++++++++++++++++++-- 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/src/api2/types/mod.rs b/src/api2/types/mod.rs index 1e239d27..5611c54c 100644 --- a/src/api2/types/mod.rs +++ b/src/api2/types/mod.rs @@ -1360,3 +1360,35 @@ pub struct KeyInfo { #[serde(skip_serializing_if="Option::is_none")] pub hint: Option, } + +#[api] +#[derive(Deserialize, Serialize)] +/// RSA public key information +pub struct RsaPubKeyInfo { + /// Path to key (if stored in a file) + #[serde(skip_serializing_if="Option::is_none")] + pub path: Option, + /// RSA exponent + pub exponent: String, + /// Hex-encoded RSA modulus + pub modulus: String, + /// Key (modulus) length in bits + pub length: usize, +} + +impl std::convert::TryFrom> for RsaPubKeyInfo { + type Error = anyhow::Error; + + fn try_from(value: openssl::rsa::Rsa) -> Result { + let modulus = value.n().to_hex_str()?.to_string(); + let exponent = value.e().to_dec_str()?.to_string(); + let length = value.size() as usize * 8; + + Ok(Self { + path: None, + exponent, + modulus, + length, + }) + } +} diff --git a/src/bin/proxmox_backup_client/key.rs b/src/bin/proxmox_backup_client/key.rs index 037ee0eb..43eaab5c 100644 --- a/src/bin/proxmox_backup_client/key.rs +++ b/src/bin/proxmox_backup_client/key.rs @@ -1,4 +1,5 @@ use std::path::PathBuf; +use std::convert::TryFrom; use anyhow::{bail, format_err, Error}; use serde_json::Value; @@ -25,6 +26,7 @@ use proxmox_backup::{ PASSWORD_HINT_SCHEMA, KeyInfo, Kdf, + RsaPubKeyInfo, }, backup::{ rsa_decrypt_key_config, @@ -366,9 +368,16 @@ fn show_key(path: Option, param: Value) -> Result<(), Error> { fn import_master_pubkey(path: String) -> Result<(), Error> { let pem_data = file_get_contents(&path)?; - if let Err(err) = openssl::pkey::PKey::public_key_from_pem(&pem_data) { - bail!("Unable to decode PEM data - {}", err); - } + match openssl::pkey::PKey::public_key_from_pem(&pem_data) { + Ok(key) => { + let info = RsaPubKeyInfo::try_from(key.rsa()?)?; + println!("Found following key at {:?}", path); + println!("Modulus: {}", info.modulus); + println!("Exponent: {}", info.exponent); + println!("Length: {}", info.length); + }, + Err(err) => bail!("Unable to decode PEM data - {}", err), + }; let target_path = place_default_master_pubkey()?; @@ -388,7 +397,18 @@ fn create_master_key() -> Result<(), Error> { bail!("unable to create master key - no tty"); } - let rsa = openssl::rsa::Rsa::generate(4096)?; + let bits = 4096; + println!("Generating {}-bit RSA key..", bits); + let rsa = openssl::rsa::Rsa::generate(bits)?; + let public = openssl::rsa::Rsa::from_public_components( + rsa.n().to_owned()?, + rsa.e().to_owned()?, + )?; + let info = RsaPubKeyInfo::try_from(public)?; + println!("Modulus: {}", info.modulus); + println!("Exponent: {}", info.exponent); + println!(); + let pkey = openssl::pkey::PKey::from_rsa(rsa)?; let password = String::from_utf8(tty::read_and_verify_password("Master Key Password: ")?)?; @@ -408,6 +428,56 @@ fn create_master_key() -> Result<(), Error> { Ok(()) } +#[api( + input: { + properties: { + path: { + description: "Path to the PEM formatted RSA public key. Default location will be used if not specified.", + optional: true, + }, + "output-format": { + schema: OUTPUT_FORMAT, + optional: true, + }, + }, + }, +)] +/// List information about master key +fn show_master_pubkey(path: Option, param: Value) -> Result<(), Error> { + let path = match path { + Some(path) => PathBuf::from(path), + None => find_default_master_pubkey()? + .ok_or_else(|| format_err!("No path specified and no default master key available."))?, + }; + + let path = path.canonicalize()?; + + let output_format = get_output_format(¶m); + + let pem_data = file_get_contents(path.clone())?; + let rsa = openssl::rsa::Rsa::public_key_from_pem(&pem_data)?; + + let mut info = RsaPubKeyInfo::try_from(rsa)?; + info.path = Some(path.display().to_string()); + + let options = proxmox::api::cli::default_table_format_options() + .column(ColumnConfig::new("path")) + .column(ColumnConfig::new("modulus")) + .column(ColumnConfig::new("exponent")) + .column(ColumnConfig::new("length")); + + let return_type = ReturnType::new(false, &RsaPubKeyInfo::API_SCHEMA); + + format_and_print_result_full( + &mut serde_json::to_value(info)?, + &return_type, + &output_format, + &options, + ); + + Ok(()) +} + #[api( input: { properties: { @@ -467,6 +537,9 @@ pub fn cli() -> CliCommandMap { let key_import_master_pubkey_cmd_def = CliCommand::new(&API_METHOD_IMPORT_MASTER_PUBKEY) .arg_param(&["path"]) .completion_cb("path", tools::complete_file_name); + let key_show_master_pubkey_cmd_def = CliCommand::new(&API_METHOD_SHOW_MASTER_PUBKEY) + .arg_param(&["path"]) + .completion_cb("path", tools::complete_file_name); let key_show_cmd_def = CliCommand::new(&API_METHOD_SHOW_KEY) .arg_param(&["path"]) @@ -483,5 +556,6 @@ pub fn cli() -> CliCommandMap { .insert("import-master-pubkey", key_import_master_pubkey_cmd_def) .insert("change-passphrase", key_change_passphrase_cmd_def) .insert("show", key_show_cmd_def) + .insert("show-master-pubkey", key_show_master_pubkey_cmd_def) .insert("paperkey", paper_key_cmd_def) }