From fe30f538bac71e90b48e2c5deb8169374b4c113b Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sat, 29 Mar 2025 17:09:13 +0800 Subject: [PATCH] feat: v1.11.14, add convert-pem-to-jwk --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/cmd_convert_pem_to_jwk.rs | 36 +++++++++++++++++++++++++++++++++++ src/cmd_sign_jwt.rs | 4 ++-- src/ecutil.rs | 33 ++++++++++++++++++++++++++++++++ src/main.rs | 3 +++ 6 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/cmd_convert_pem_to_jwk.rs create mode 100644 src/ecutil.rs diff --git a/Cargo.lock b/Cargo.lock index 37557db..ed249cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -508,7 +508,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.11.13" +version = "1.11.14" dependencies = [ "aes-gcm-stream", "authenticator 0.3.1", diff --git a/Cargo.toml b/Cargo.toml index 6bd4f8d..ce13adc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.11.13" +version = "1.11.14" authors = ["Hatter Jiang "] edition = "2018" diff --git a/src/cmd_convert_pem_to_jwk.rs b/src/cmd_convert_pem_to_jwk.rs new file mode 100644 index 0000000..41cd132 --- /dev/null +++ b/src/cmd_convert_pem_to_jwk.rs @@ -0,0 +1,36 @@ +use crate::{ecutil, util}; +use clap::{App, Arg, ArgMatches, SubCommand}; +use rust_util::util_clap::{Command, CommandError}; +use serde_json::Value; + +pub struct CommandImpl; + +impl Command for CommandImpl { + fn name(&self) -> &str { + "convert-pem-to-jwk" + } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()) + .about("Convert PEM to JWK") + .arg( + Arg::with_name("public-key") + .long("public-key") + .required(true) + .takes_value(true) + .help("Public key (PEM, base64(DER) format)"), + ) + } + + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let public_key = sub_arg_matches.value_of("public-key").unwrap(); + + let jwk = ecutil::convert_ec_public_key_to_jwk(public_key)?; + + let jwk_value: Value = serde_json::from_str(&jwk).unwrap(); + + util::print_pretty_json(&jwk_value); + + Ok(None) + } +} diff --git a/src/cmd_sign_jwt.rs b/src/cmd_sign_jwt.rs index 962b7f1..755cc3f 100644 --- a/src/cmd_sign_jwt.rs +++ b/src/cmd_sign_jwt.rs @@ -131,8 +131,8 @@ pub fn digest_by_jwt_algorithm(jwt_algorithm: AlgorithmType, tobe_signed: &[u8]) AlgorithmType::Rs256 => { rsautil::pkcs15_sha256_rsa_2048_padding_for_sign(&digestutil::sha256_bytes(tobe_signed)) } - AlgorithmType::Es256 => digestutil::sha256_bytes(&tobe_signed), - AlgorithmType::Es384 => digestutil::sha384_bytes(&tobe_signed), + AlgorithmType::Es256 => digestutil::sha256_bytes(tobe_signed), + AlgorithmType::Es384 => digestutil::sha384_bytes(tobe_signed), _ => return simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm), }) } diff --git a/src/ecutil.rs b/src/ecutil.rs new file mode 100644 index 0000000..1ce35f0 --- /dev/null +++ b/src/ecutil.rs @@ -0,0 +1,33 @@ +use crate::util::base64_decode; +use rust_util::XResult; +use spki::DecodePublicKey; + +pub fn convert_ec_public_key_to_jwk(public_key: &str) -> XResult { + if let Ok(jwk) = convert_ec_public_key_p256_to_jwk(public_key) { + return Ok(jwk); + } + if let Ok(jwk) = convert_ec_public_key_p384_to_jwk(public_key) { + return Ok(jwk); + } + simple_error!("Parse public key failed, MUST be pem or base64 encoded DER.") +} + +pub fn convert_ec_public_key_p256_to_jwk(public_key: &str) -> XResult { + let public_key_p256 = if public_key.contains("BEGIN PUBLIC KEY") { + p256::PublicKey::from_public_key_pem(public_key)? + } else { + let der = base64_decode(public_key)?; + p256::PublicKey::from_public_key_der(&der)? + }; + Ok(public_key_p256.to_jwk_string()) +} + +pub fn convert_ec_public_key_p384_to_jwk(public_key: &str) -> XResult { + let public_key_p384 = if public_key.contains("BEGIN PUBLIC KEY") { + p384::PublicKey::from_public_key_pem(public_key)? + } else { + let der = base64_decode(public_key)?; + p384::PublicKey::from_public_key_der(&der)? + }; + Ok(public_key_p384.to_jwk_string()) +} diff --git a/src/main.rs b/src/main.rs index 9043896..0f47e12 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,6 +72,8 @@ mod sshutil; mod util; mod keychain; mod cmdutil; +mod cmd_convert_pem_to_jwk; +mod ecutil; pub struct DefaultCommandImpl; @@ -149,6 +151,7 @@ fn inner_main() -> CommandError { Box::new(cmd_keypair_generate::CommandImpl), Box::new(cmd_keypair_keychain_import::CommandImpl), Box::new(cmd_keypair_keychain_export::CommandImpl), + Box::new(cmd_convert_pem_to_jwk::CommandImpl), ]; #[allow(clippy::vec_init_then_push)]