diff --git a/.gitignore b/.gitignore index 5e5bde1..923c8a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +__*.pem test.txt enc.txt enc_key.pem diff --git a/Cargo.lock b/Cargo.lock index 36eaa94..61ca983 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -384,7 +384,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.1.7" +version = "1.1.8" dependencies = [ "authenticator", "base64 0.13.0", diff --git a/Cargo.toml b/Cargo.toml index 498127f..5daa79f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.1.7" +version = "1.1.8" authors = ["Hatter Jiang "] edition = "2018" diff --git a/src/cmd_rsadecrypt.rs b/src/cmd_rsadecrypt.rs new file mode 100644 index 0000000..26bea29 --- /dev/null +++ b/src/cmd_rsadecrypt.rs @@ -0,0 +1,82 @@ +use std::collections::BTreeMap; + +use clap::{App, Arg, ArgMatches, SubCommand}; +use openssl::encrypt::Decrypter; +use openssl::pkey::PKey; +use openssl::rsa::{Padding, Rsa}; +use rust_util::util_clap::{Command, CommandError}; + +pub struct CommandImpl; + +// https://docs.rs/openssl/0.10.36/openssl/encrypt/index.html +impl Command for CommandImpl { + fn name(&self) -> &str { "rsa-decrypt" } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("RSA Decrypt subcommand") + .arg(Arg::with_name("pri-key-in").long("pri-key-in").takes_value(true).help("Private key in")) + .arg(Arg::with_name("encrypted").long("encrypted").takes_value(true).help("Encrypted data")) + .arg(Arg::with_name("padding").long("padding").takes_value(true) + .possible_values(&["pkcs1", "oaep", "pss", "none"]).help("Padding")) + .arg(Arg::with_name("json").long("json").help("JSON output")) + } + + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let json_output = sub_arg_matches.is_present("json"); + if json_output { rust_util::util_msg::set_logger_std_out(false); } + + let pri_key_in = opt_value_result!(sub_arg_matches.value_of("pri-key-in"), "Require private key in"); + let pri_key_bytes = opt_result!(std::fs::read(pri_key_in), "Read file: {}, failed: {}", pri_key_in); + + let padding_opt = sub_arg_matches.value_of("padding"); + + let padding = match padding_opt { + Some("oaep") | Some("pkcs1_oaep") => Padding::PKCS1_OAEP, + Some("pss") | Some("pkcs1_pss") => Padding::PKCS1_PSS, + Some("none") => Padding::NONE, + Some("pkcs1") | None => Padding::PKCS1, + Some(p) => return simple_error!("Not supported padding: {}", p), + }; + let padding_str = match padding { + Padding::NONE => "none", + Padding::PKCS1 => "pkcs1", + Padding::PKCS1_PSS => "pkcs1_pss", + Padding::PKCS1_OAEP => "pkcs1_oaep", + _ => "unknown", + }; + + let mut json = BTreeMap::new(); + + let keypair = opt_result!(Rsa::private_key_from_pem(&pri_key_bytes), "Parse RSA failed: {}"); + let keypair = opt_result!(PKey::from_rsa(keypair), "RSA to PKey failed: {}"); + + let encrypted = if let Some(encrypted) = sub_arg_matches.value_of("encrypted") { + opt_result!(hex::decode(encrypted), "Decode encrypted HEX failed: {}") + } else { + return simple_error!("Data is required, --data-hex or --data argument!"); + }; + + let mut decrypter = opt_result!(Decrypter::new(&keypair), "Decrypter new failed: {}"); + opt_result!(decrypter.set_rsa_padding(padding), "Set RSA padding failed: {}"); + let buffer_len = opt_result!(decrypter.decrypt_len(&encrypted), "Decrypt len failed: {}"); + let mut data = vec![0; buffer_len]; + let decrypted_len = opt_result!(decrypter.decrypt(&encrypted, &mut data), "Decrypt failed: {}"); + data.truncate(decrypted_len); + + let encrypted_hex = hex::encode(&encrypted); + information!("Padding: {}", padding_str); + success!("Message HEX: {}", hex::encode(&data)); + success!("Message: {}", String::from_utf8_lossy(&data)); + if json_output { + json.insert("data", hex::encode(&data)); + json.insert("padding", padding_str.to_string()); + json.insert("encrypted", encrypted_hex); + } + + if json_output { + println!("{}", serde_json::to_string_pretty(&json).unwrap()); + } + + Ok(None) + } +} diff --git a/src/main.rs b/src/main.rs index 4ebf35d..31b14a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ mod pgpcardutil; mod cmd_u2fregister; mod cmd_u2fsign; mod cmd_rsaencrypt; +mod cmd_rsadecrypt; mod cmd_pgp; mod cmd_pgpcardadmin; mod cmd_pgpcardlist; @@ -46,6 +47,7 @@ fn inner_main() -> CommandError { Box::new(cmd_chall::CommandImpl), Box::new(cmd_challconfig::CommandImpl), Box::new(cmd_rsaencrypt::CommandImpl), + Box::new(cmd_rsadecrypt::CommandImpl), Box::new(cmd_pgp::CommandImpl), Box::new(cmd_pgpcardadmin::CommandImpl), Box::new(cmd_pgpcardlist::CommandImpl),