client: add 'import-with-master-key' command
to import an encrypted encryption key using a master key. Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
parent
8acfd15d6e
commit
7137630d43
@ -259,3 +259,15 @@ pub fn rsa_encrypt_key_config(
|
|||||||
}
|
}
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rsa_decrypt_key_config(
|
||||||
|
rsa: openssl::rsa::Rsa<openssl::pkey::Private>,
|
||||||
|
key: &[u8],
|
||||||
|
passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
|
||||||
|
) -> Result<([u8; 32], i64, Fingerprint), Error> {
|
||||||
|
let mut buffer = vec![0u8; rsa.size() as usize];
|
||||||
|
let decrypted = rsa
|
||||||
|
.private_decrypt(key, &mut buffer, openssl::rsa::Padding::PKCS1)
|
||||||
|
.map_err(|err| format_err!("failed to decrypt KeyConfig using RSA - {}", err))?;
|
||||||
|
decrypt_key(&mut buffer[..decrypted], passphrase)
|
||||||
|
}
|
||||||
|
@ -1081,14 +1081,6 @@ async fn create_backup(
|
|||||||
.await?;
|
.await?;
|
||||||
manifest.add_file(target.to_string(), stats.size, stats.csum, crypt_mode)?;
|
manifest.add_file(target.to_string(), stats.size, stats.csum, crypt_mode)?;
|
||||||
|
|
||||||
// openssl rsautl -decrypt -inkey master-private.pem -in rsa-encrypted.key -out t
|
|
||||||
/*
|
|
||||||
let mut buffer2 = vec![0u8; rsa.size() as usize];
|
|
||||||
let pem_data = file_get_contents("master-private.pem")?;
|
|
||||||
let rsa = openssl::rsa::Rsa::private_key_from_pem(&pem_data)?;
|
|
||||||
let len = rsa.private_decrypt(&buffer, &mut buffer2, openssl::rsa::Padding::PKCS1)?;
|
|
||||||
println!("TEST {} {:?}", len, buffer2);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
// create manifest (index.json)
|
// create manifest (index.json)
|
||||||
// manifests are never encrypted, but include a signature
|
// manifests are never encrypted, but include a signature
|
||||||
|
@ -21,12 +21,14 @@ use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
|
|||||||
use proxmox_backup::backup::{
|
use proxmox_backup::backup::{
|
||||||
encrypt_key_with_passphrase,
|
encrypt_key_with_passphrase,
|
||||||
load_and_decrypt_key,
|
load_and_decrypt_key,
|
||||||
|
rsa_decrypt_key_config,
|
||||||
store_key_config,
|
store_key_config,
|
||||||
CryptConfig,
|
CryptConfig,
|
||||||
Kdf,
|
Kdf,
|
||||||
KeyConfig,
|
KeyConfig,
|
||||||
KeyDerivationConfig,
|
KeyDerivationConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
use proxmox_backup::tools;
|
use proxmox_backup::tools;
|
||||||
|
|
||||||
#[api()]
|
#[api()]
|
||||||
@ -152,6 +154,90 @@ fn create(kdf: Option<Kdf>, path: Option<String>) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[api(
|
||||||
|
input: {
|
||||||
|
properties: {
|
||||||
|
"master-keyfile": {
|
||||||
|
description: "(Private) master key to use.",
|
||||||
|
},
|
||||||
|
"encrypted-keyfile": {
|
||||||
|
description: "RSA-encrypted keyfile to import.",
|
||||||
|
},
|
||||||
|
kdf: {
|
||||||
|
type: Kdf,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
description:
|
||||||
|
"Output file. Without this the key will become the new default encryption key.",
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)]
|
||||||
|
/// Import an encrypted backup of an encryption key using a (private) master key.
|
||||||
|
async fn import_with_master_key(
|
||||||
|
master_keyfile: String,
|
||||||
|
encrypted_keyfile: String,
|
||||||
|
kdf: Option<Kdf>,
|
||||||
|
path: Option<String>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let path = match path {
|
||||||
|
Some(path) => PathBuf::from(path),
|
||||||
|
None => {
|
||||||
|
let path = place_default_encryption_key()?;
|
||||||
|
if path.exists() {
|
||||||
|
bail!("Please remove default encryption key at {:?} before importing to default location (or choose a non-default one).", path);
|
||||||
|
}
|
||||||
|
println!("Importing key to default location at: {:?}", path);
|
||||||
|
path
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let encrypted_key = file_get_contents(&encrypted_keyfile)?;
|
||||||
|
let master_key = file_get_contents(&master_keyfile)?;
|
||||||
|
let password = tty::read_password("Master Key Password: ")?;
|
||||||
|
|
||||||
|
let master_key =
|
||||||
|
openssl::pkey::PKey::private_key_from_pem_passphrase(&master_key, &password)
|
||||||
|
.map_err(|err| format_err!("failed to read PEM-formatted private key - {}", err))?
|
||||||
|
.rsa()
|
||||||
|
.map_err(|err| format_err!("not a valid private RSA key - {}", err))?;
|
||||||
|
|
||||||
|
let (key, created, fingerprint) =
|
||||||
|
rsa_decrypt_key_config(master_key, &encrypted_key, &get_encryption_key_password)?;
|
||||||
|
|
||||||
|
let kdf = kdf.unwrap_or_default();
|
||||||
|
match kdf {
|
||||||
|
Kdf::None => {
|
||||||
|
let modified = proxmox::tools::time::epoch_i64();
|
||||||
|
|
||||||
|
store_key_config(
|
||||||
|
&path,
|
||||||
|
true,
|
||||||
|
KeyConfig {
|
||||||
|
kdf: None,
|
||||||
|
created, // keep original value
|
||||||
|
modified,
|
||||||
|
data: key.to_vec(),
|
||||||
|
fingerprint: Some(fingerprint),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Kdf::Scrypt | Kdf::PBKDF2 => {
|
||||||
|
let password = tty::read_and_verify_password("New Password: ")?;
|
||||||
|
|
||||||
|
let mut new_key_config = encrypt_key_with_passphrase(&key, &password, kdf)?;
|
||||||
|
new_key_config.created = created; // keep original value
|
||||||
|
new_key_config.fingerprint = Some(fingerprint);
|
||||||
|
|
||||||
|
store_key_config(&path, true, new_key_config)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
@ -446,6 +532,14 @@ pub fn cli() -> CliCommandMap {
|
|||||||
.arg_param(&["path"])
|
.arg_param(&["path"])
|
||||||
.completion_cb("path", tools::complete_file_name);
|
.completion_cb("path", tools::complete_file_name);
|
||||||
|
|
||||||
|
let key_import_with_master_key_cmd_def = CliCommand::new(&API_METHOD_IMPORT_WITH_MASTER_KEY)
|
||||||
|
.arg_param(&["master-keyfile"])
|
||||||
|
.completion_cb("master-keyfile", tools::complete_file_name)
|
||||||
|
.arg_param(&["encrypted-keyfile"])
|
||||||
|
.completion_cb("encrypted-keyfile", tools::complete_file_name)
|
||||||
|
.arg_param(&["path"])
|
||||||
|
.completion_cb("path", tools::complete_file_name);
|
||||||
|
|
||||||
let key_change_passphrase_cmd_def = CliCommand::new(&API_METHOD_CHANGE_PASSPHRASE)
|
let key_change_passphrase_cmd_def = CliCommand::new(&API_METHOD_CHANGE_PASSPHRASE)
|
||||||
.arg_param(&["path"])
|
.arg_param(&["path"])
|
||||||
.completion_cb("path", tools::complete_file_name);
|
.completion_cb("path", tools::complete_file_name);
|
||||||
@ -465,6 +559,7 @@ pub fn cli() -> CliCommandMap {
|
|||||||
|
|
||||||
CliCommandMap::new()
|
CliCommandMap::new()
|
||||||
.insert("create", key_create_cmd_def)
|
.insert("create", key_create_cmd_def)
|
||||||
|
.insert("import-with-master-key", key_import_with_master_key_cmd_def)
|
||||||
.insert("create-master-key", key_create_master_key_cmd_def)
|
.insert("create-master-key", key_create_master_key_cmd_def)
|
||||||
.insert("import-master-pubkey", key_import_master_pubkey_cmd_def)
|
.insert("import-master-pubkey", key_import_master_pubkey_cmd_def)
|
||||||
.insert("change-passphrase", key_change_passphrase_cmd_def)
|
.insert("change-passphrase", key_change_passphrase_cmd_def)
|
||||||
|
Loading…
Reference in New Issue
Block a user