From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 41E9B60584 for ; Fri, 5 Feb 2021 16:36:37 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 38991AED9 for ; Fri, 5 Feb 2021 16:36:07 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 7C840AECF for ; Fri, 5 Feb 2021 16:36:06 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 4844A461F3 for ; Fri, 5 Feb 2021 16:36:06 +0100 (CET) From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= To: pbs-devel@lists.proxmox.com Date: Fri, 5 Feb 2021 16:35:28 +0100 Message-Id: <20210205153535.2578184-4-f.gruenbichler@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210205153535.2578184-1-f.gruenbichler@proxmox.com> References: <20210205153535.2578184-1-f.gruenbichler@proxmox.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.026 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [mod.rs, key.rs] Subject: [pbs-devel] [PATCH proxmox-backup 02/10] key: add show-master-pubkey command X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 05 Feb 2021 15:36:37 -0000 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) } -- 2.20.1