feat: v1.12.6

This commit is contained in:
2025-05-05 11:22:28 +08:00
parent e52e42d48c
commit 8894a2156a
11 changed files with 302 additions and 76 deletions

2
Cargo.lock generated
View File

@@ -508,7 +508,7 @@ dependencies = [
[[package]]
name = "card-cli"
version = "1.12.5"
version = "1.12.6"
dependencies = [
"aes-gcm-stream",
"authenticator 0.3.1",

View File

@@ -1,6 +1,6 @@
[package]
name = "card-cli"
version = "1.12.5"
version = "1.12.6"
authors = ["Hatter Jiang <jht5945@gmail.com>"]
edition = "2018"

26
examples/rsa.rs Normal file
View File

@@ -0,0 +1,26 @@
use base64::Engine;
use rand::rngs::OsRng;
use rsa::pkcs1::LineEnding;
use rsa::pkcs8::DecodePrivateKey;
use rsa::pkcs8::EncodePrivateKey;
use rsa::traits::PublicKeyParts;
use rsa::RsaPrivateKey;
use spki::EncodePublicKey;
fn main() {
let key = RsaPrivateKey::new(&mut OsRng, 1024).unwrap();
let pem = key.to_pkcs8_pem(LineEnding::LF).unwrap();
println!("{}", pem.as_str());
let key2 = RsaPrivateKey::from_pkcs8_pem(pem.as_ref()).unwrap();
let pub_key = key2.to_public_key();
let public_key_pem = pub_key.to_public_key_pem(LineEnding::LF).unwrap();
println!("{}", public_key_pem);
let n = pub_key.n();
let e = pub_key.e();
let url_safe = base64::engine::general_purpose::URL_SAFE_NO_PAD;
println!("n: {}", url_safe.encode(&n.to_bytes_be()));
println!("e: {}", url_safe.encode(&e.to_bytes_be()));
}

View File

@@ -1,5 +1,5 @@
use crate::keyutil::{parse_key_uri, KeyUri, KeyUsage};
use crate::util::base64_encode;
use crate::util::{base64_decode, base64_encode};
use crate::yubikeyutil::find_key_or_error;
use crate::{cmdutil, ecdsautil, hmacutil, seutil, util, yubikeyutil};
use clap::{App, ArgMatches, SubCommand};
@@ -8,7 +8,10 @@ use rust_util::util_clap::{Command, CommandError};
use rust_util::XResult;
use serde_json::Value;
use std::collections::BTreeMap;
use rsa::RsaPrivateKey;
use spki::EncodePublicKey;
use x509_parser::parse_x509_certificate;
use crate::pivutil::ToStr;
pub struct CommandImpl;
@@ -68,21 +71,31 @@ fn fetch_public_key(parameter: &str, serial_opt: &Option<&str>) -> XResult<Vec<u
simple_error!("Slot {} not found", key.slot)
}
KeyUri::YubikeyHmacEncSoftKey(key) => {
let private_key = hmacutil::try_hmac_decrypt_to_string(&key.hmac_enc_private_key)?;
let p256_public_key = ecdsautil::parse_p256_private_key_to_public_key(&private_key).ok();
let p384_public_key = ecdsautil::parse_p384_private_key_to_public_key(&private_key).ok();
let p521_public_key = ecdsautil::parse_p521_private_key_to_public_key(&private_key).ok();
if key.algorithm.is_ecc() {
let private_key = hmacutil::try_hmac_decrypt_to_string(&key.hmac_enc_private_key)?;
let p256_public_key = ecdsautil::parse_p256_private_key_to_public_key(&private_key).ok();
let p384_public_key = ecdsautil::parse_p384_private_key_to_public_key(&private_key).ok();
let p521_public_key = ecdsautil::parse_p521_private_key_to_public_key(&private_key).ok();
if let Some(p256_public_key) = p256_public_key {
return Ok(p256_public_key);
if let Some(p256_public_key) = p256_public_key {
return Ok(p256_public_key);
}
if let Some(p384_public_key) = p384_public_key {
return Ok(p384_public_key);
}
if let Some(p521_public_key) = p521_public_key {
return Ok(p521_public_key);
}
simple_error!("Invalid hmac enc private key")
} else if key.algorithm.is_rsa() {
use rsa::pkcs8::DecodePrivateKey;
let private_key = hmacutil::try_hmac_decrypt_to_string(&key.hmac_enc_private_key)?;
let private_key_der = base64_decode(&private_key)?;
let rsa_private_key = RsaPrivateKey::from_pkcs8_der(&private_key_der)?;
Ok(rsa_private_key.to_public_key().to_public_key_der()?.to_vec())
} else {
simple_error!("Invalid algorithm: {}", key.algorithm.to_str())
}
if let Some(p384_public_key) = p384_public_key {
return Ok(p384_public_key);
}
if let Some(p521_public_key) = p521_public_key {
return Ok(p521_public_key);
}
simple_error!("Invalid hmac enc private key")
}
}
}

View File

@@ -1,17 +1,19 @@
use crate::cmd_sign_jwt::digest_by_jwt_algorithm;
use crate::keyutil::{parse_key_uri, KeyUri, KeyUsage, YubikeyPivKey};
use crate::cmd_sign_jwt_soft::{convert_jwt_algorithm_to_ecdsa_algorithm, parse_ecdsa_private_key};
use crate::ecdsautil::EcdsaSignType;
use crate::keyutil::{parse_key_uri, KeyAlgorithmId, KeyUri, KeyUsage, YubikeyPivKey};
use crate::pivutil::ToStr;
use crate::rsautil::RsaSignAlgorithm;
use crate::util::{base64_decode, base64_encode};
use crate::{cmdutil, ecdsautil, hmacutil, pivutil, seutil, util, yubikeyutil};
use crate::{cmdutil, ecdsautil, hmacutil, pivutil, rsautil, seutil, util, yubikeyutil};
use clap::{App, ArgMatches, SubCommand};
use jwt::AlgorithmType;
use rsa::RsaPrivateKey;
use rust_util::util_clap::{Command, CommandError};
use rust_util::XResult;
use serde_json::Value;
use std::collections::BTreeMap;
use yubikey::piv::{sign_data, AlgorithmId};
use crate::cmd_sign_jwt_soft::{convert_jwt_algorithm_to_ecdsa_algorithm, parse_ecdsa_private_key};
use crate::ecdsautil::EcdsaSignType;
use yubikey::piv::sign_data;
pub struct CommandImpl;
@@ -79,22 +81,45 @@ fn sign(sub_arg_matches: &ArgMatches) -> XResult<Vec<u8>> {
);
}
let algorithm = opt_value_result!(
KeyAlgorithmId::to_algorithm_id(key.algorithm),
"Yubikey not supported algorithm: {}",
key.algorithm.to_str()
);
let raw_in = digest_by_jwt_algorithm(jwt_algorithm, &message_bytes)?;
let signed_data = opt_result!(
sign_data(&mut yk, &raw_in, key.algorithm, key.slot),
sign_data(&mut yk, &raw_in, algorithm, key.slot),
"Sign YubiKey failed: {}"
);
Ok(signed_data.to_vec())
}
KeyUri::YubikeyHmacEncSoftKey(key) => {
let private_key = hmacutil::try_hmac_decrypt_to_string(&key.hmac_enc_private_key)?;
let (jwt_algorithm, private_key_d) = parse_ecdsa_private_key(&private_key)?;
if key.algorithm.is_ecc() {
let private_key = hmacutil::try_hmac_decrypt_to_string(&key.hmac_enc_private_key)?;
let (jwt_algorithm, private_key_d) = parse_ecdsa_private_key(&private_key)?;
let raw_in = digest_by_jwt_algorithm(jwt_algorithm, &message_bytes)?;
let ecdsa_algorithm = convert_jwt_algorithm_to_ecdsa_algorithm(jwt_algorithm)?;
let signed_data = ecdsautil::ecdsa_sign(ecdsa_algorithm, &private_key_d, &raw_in, EcdsaSignType::Der)?;
let raw_in = digest_by_jwt_algorithm(jwt_algorithm, &message_bytes)?;
let ecdsa_algorithm = convert_jwt_algorithm_to_ecdsa_algorithm(jwt_algorithm)?;
let signed_data = ecdsautil::ecdsa_sign(
ecdsa_algorithm,
&private_key_d,
&raw_in,
EcdsaSignType::Der,
)?;
Ok(signed_data)
Ok(signed_data)
} else if key.algorithm.is_rsa() {
use rsa::pkcs8::DecodePrivateKey;
let private_key = hmacutil::try_hmac_decrypt_to_string(&key.hmac_enc_private_key)?;
let private_key_der = base64_decode(&private_key)?;
let rsa_private_key = RsaPrivateKey::from_pkcs8_der(&private_key_der)?;
let rsa_sign_algorithm =
opt_value_result!(RsaSignAlgorithm::from_str(alg), "Invalid --alg: {}", alg);
rsautil::sign(&rsa_private_key, rsa_sign_algorithm, &message_bytes)
} else {
simple_error!("Invalid algorithm: {}", key.algorithm.to_str())
}
}
}
}
@@ -103,20 +128,23 @@ fn get_jwt_algorithm(key: &YubikeyPivKey, alg: &str) -> XResult<AlgorithmType> {
let jwt_algorithm = match alg {
"ES256" => AlgorithmType::Es256,
"ES384" => AlgorithmType::Es384,
"ES512" => AlgorithmType::Es512,
"RS256" => AlgorithmType::Rs256,
_ => return simple_error!("Invalid alg: {}", alg),
};
if key.algorithm == AlgorithmId::Rsa1024 {
if key.algorithm == KeyAlgorithmId::Rsa1024 {
return simple_error!("Invalid algorithm: RSA1024");
}
let is_p256_mismatch =
key.algorithm == AlgorithmId::EccP256 && jwt_algorithm != AlgorithmType::Es256;
key.algorithm == KeyAlgorithmId::EccP256 && jwt_algorithm != AlgorithmType::Es256;
let is_p384_mismatch =
key.algorithm == AlgorithmId::EccP384 && jwt_algorithm != AlgorithmType::Es384;
key.algorithm == KeyAlgorithmId::EccP384 && jwt_algorithm != AlgorithmType::Es384;
let is_p521_mismatch =
key.algorithm == KeyAlgorithmId::EccP521 && jwt_algorithm != AlgorithmType::Es512;
let is_rsa_mismatch =
key.algorithm == AlgorithmId::Rsa2048 && jwt_algorithm != AlgorithmType::Rs256;
key.algorithm == KeyAlgorithmId::Rsa2048 && jwt_algorithm != AlgorithmType::Rs256;
if is_p256_mismatch || is_p384_mismatch || is_rsa_mismatch {
if is_p256_mismatch || is_p384_mismatch || is_p521_mismatch || is_rsa_mismatch {
return simple_error!("Invalid algorithm: {} vs {}", key.algorithm.to_str(), alg);
}
Ok(jwt_algorithm)

View File

@@ -1,12 +1,11 @@
use crate::ecdsautil::EcdsaAlgorithm;
use crate::keychain::{KeychainKey, KeychainKeyValue};
use crate::{cmdutil, ecdsautil, hmacutil, util, yubikeyutil};
use crate::keyutil::{KeyAlgorithmId, KeyUri, YubikeyHmacEncSoftKey};
use crate::util::base64_encode;
use crate::{cmdutil, ecdsautil, hmacutil, rsautil, util, yubikeyutil};
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use std::collections::BTreeMap;
use yubikey::piv::AlgorithmId;
use crate::ecdsautil::EcdsaAlgorithm;
use crate::keyutil::{KeyUri, YubikeyHmacEncSoftKey};
use crate::util::base64_encode;
pub struct CommandImpl;
@@ -23,7 +22,7 @@ impl Command for CommandImpl {
.long("type")
.required(true)
.takes_value(true)
.help("Key type (e.g. p256, p384, p521)"),
.help("Key type (e.g. p256, p384, p521, rsa1024, rsa2048)"),
)
.arg(
Arg::with_name("with-hmac-encrypt")
@@ -49,20 +48,26 @@ impl Command for CommandImpl {
}
let ecdsa_algorithm = match key_type.as_str() {
"p256" => EcdsaAlgorithm::P256,
"p384" => EcdsaAlgorithm::P384,
"p521" => EcdsaAlgorithm::P521,
_ => {
return simple_error!("Key type must be p256 or p384");
}
"p256" => Some(EcdsaAlgorithm::P256),
"p384" => Some(EcdsaAlgorithm::P384),
"p521" => Some(EcdsaAlgorithm::P521),
_ => None,
};
let rsa_bit_size: Option<usize> = match key_type.as_str() {
"rsa1024" => Some(1024),
"rsa2048" => Some(2048),
_ => None,
};
let (pkcs8_base64,
secret_key_pem,
public_key_pem,
public_key_der,
jwk_ec_key
) = ecdsautil::generate_ecdsa_keypair(ecdsa_algorithm)?;
let (
pkcs8_base64, secret_key_pem, public_key_pem, public_key_der, jwk_key
) = if let Some(ecdsa_algorithm) = ecdsa_algorithm {
ecdsautil::generate_ecdsa_keypair(ecdsa_algorithm)?
} else if let Some(rsa_bit_size) = rsa_bit_size {
rsautil::generate_rsa_keypair(rsa_bit_size)?
} else {
return simple_error!("Unsupported key type: {}", key_type);
};
let (pkcs8_base64, secret_key_pem) = if with_hmac_encrypt {
(
@@ -80,7 +85,7 @@ impl Command for CommandImpl {
pkcs8_base64: pkcs8_base64.clone(),
secret_key_pem: secret_key_pem.clone(),
public_key_pem: public_key_pem.clone(),
public_key_jwk: jwk_ec_key.to_string(),
public_key_jwk: jwk_key.clone(),
};
let keychain_key_value_json = serde_json::to_string(&keychain_key_value)?;
@@ -98,18 +103,24 @@ impl Command for CommandImpl {
json.insert("private_key_base64", pkcs8_base64.clone());
json.insert("private_key_pem", secret_key_pem);
let algorithm_id = match key_type.as_str() {
"p256" => Some(AlgorithmId::EccP256),
"p384" => Some(AlgorithmId::EccP384),
"p256" => Some(KeyAlgorithmId::EccP256),
"p384" => Some(KeyAlgorithmId::EccP384),
"p521" => Some(KeyAlgorithmId::EccP521),
"rsa1024" => Some(KeyAlgorithmId::Rsa1024),
"rsa2048" => Some(KeyAlgorithmId::Rsa2048),
_ => None,
};
if let (true, Some(algorithm_id)) = (with_hmac_encrypt, algorithm_id) {
let yk = yubikeyutil::open_yubikey()?;
let yubikey_hmac_enc_soft_key = YubikeyHmacEncSoftKey {
key_name: format!("yubikey{}-{}", yk.version().major, yk.serial().0),
key_name: format!("yubikey{}-{}", yk.version().major, yk.serial().0),
algorithm: algorithm_id,
hmac_enc_private_key: pkcs8_base64,
};
json.insert("key_uri", KeyUri::YubikeyHmacEncSoftKey(yubikey_hmac_enc_soft_key).to_string());
json.insert(
"key_uri",
KeyUri::YubikeyHmacEncSoftKey(yubikey_hmac_enc_soft_key).to_string(),
);
}
}
Some(keychain_key_uri) => {
@@ -118,7 +129,7 @@ impl Command for CommandImpl {
}
json.insert("public_key_pem", public_key_pem);
json.insert("public_key_base64", public_key_base64);
json.insert("public_key_jwk", jwk_ec_key.to_string());
json.insert("public_key_jwk", jwk_key);
util::print_pretty_json(&json);
} else {
@@ -133,7 +144,7 @@ impl Command for CommandImpl {
}
information!("Public key PEM:\n{}", public_key_pem);
information!("Public key Base64:\n{}\n", public_key_base64);
information!("Public key JWK:\n{}", jwk_ec_key.to_string());
information!("Public key JWK:\n{}", jwk_key);
}
Ok(None)

View File

@@ -10,7 +10,7 @@ use yubikey::Key;
use yubikey::piv::{AlgorithmId, metadata};
use crate::{cmdutil, pivutil, util, yubikeyutil};
use crate::keyutil::{KeyUri, YubikeyPivKey};
use crate::keyutil::{KeyAlgorithmId, KeyUri, YubikeyPivKey};
use crate::pivutil::{get_algorithm_id_by_certificate, slot_equals, ToStr};
use crate::pkiutil::bytes_to_pem;
use crate::sshutil::SshVecWriter;
@@ -106,7 +106,7 @@ impl Command for CommandImpl {
let yubikey_piv_key = YubikeyPivKey {
key_name: format!("yubikey{}-{}", yk.version().major, yk.serial().0),
algorithm: algorithm_id,
algorithm: KeyAlgorithmId::from_algorithm_id(algorithm_id),
slot: slot_id,
};
json.insert("key_uri", KeyUri::YubikeyPivKey(yubikey_piv_key).to_string());

View File

@@ -133,6 +133,7 @@ pub fn digest_by_jwt_algorithm(jwt_algorithm: AlgorithmType, tobe_signed: &[u8])
}
AlgorithmType::Es256 => digestutil::sha256_bytes(tobe_signed),
AlgorithmType::Es384 => digestutil::sha384_bytes(tobe_signed),
AlgorithmType::Es512 => digestutil::sha512_bytes(tobe_signed),
_ => return simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm),
})
}

View File

@@ -3,7 +3,6 @@ use ecdsa::elliptic_curve::pkcs8::LineEnding;
use ecdsa::VerifyingKey;
use p256::NistP256;
use p256::ecdsa::signature::hazmat::PrehashVerifier;
use p256::elliptic_curve::JwkEcKey;
use p384::NistP384;
use p256::pkcs8::EncodePrivateKey;
use p521::NistP521;
@@ -78,12 +77,12 @@ macro_rules! generate_inner_ecdsa_keypair {
let secret_key_pem = secret_key.to_pkcs8_pem(LineEnding::LF)?.to_string();
let public_key_pem = secret_key.public_key().to_public_key_pem(LineEnding::LF)?;
let public_key_der = secret_key.public_key().to_public_key_der()?.to_vec();
let jwk_ec_key = secret_key.public_key().to_jwk();
let jwk_ec_key = secret_key.public_key().to_jwk().to_string();
Ok((secret_key_der_base64, secret_key_pem, public_key_pem, public_key_der, jwk_ec_key))
})
}
pub fn generate_ecdsa_keypair(algo: EcdsaAlgorithm) -> XResult<(String, String, String, Vec<u8>, JwkEcKey)> {
pub fn generate_ecdsa_keypair(algo: EcdsaAlgorithm) -> XResult<(String, String, String, Vec<u8>, String)> {
match algo {
EcdsaAlgorithm::P256 => generate_inner_ecdsa_keypair!(p256),
EcdsaAlgorithm::P384 => generate_inner_ecdsa_keypair!(p384),

View File

@@ -1,7 +1,7 @@
use crate::pivutil::{FromStr, ToStr};
use regex::Regex;
use rust_util::XResult;
use yubikey::piv::{AlgorithmId, SlotId};
use crate::pivutil::{ToStr, FromStr};
// reference: https://git.hatter.ink/hatter/card-cli/issues/6
#[derive(Debug)]
@@ -55,6 +55,78 @@ impl ToString for KeyUri {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum KeyAlgorithmId {
Rsa1024,
Rsa2048,
EccP256,
EccP384,
EccP521,
}
impl KeyAlgorithmId {
pub fn from_algorithm_id(algorithm_id: AlgorithmId) -> Self {
match algorithm_id {
AlgorithmId::Rsa1024 => Self::Rsa1024,
AlgorithmId::Rsa2048 => Self::Rsa2048,
AlgorithmId::EccP256 => Self::EccP256,
AlgorithmId::EccP384 => Self::EccP384,
}
}
pub fn to_algorithm_id(self) -> Option<AlgorithmId> {
match self {
KeyAlgorithmId::Rsa1024 => Some(AlgorithmId::Rsa1024),
KeyAlgorithmId::Rsa2048 => Some(AlgorithmId::Rsa2048),
KeyAlgorithmId::EccP256 => Some(AlgorithmId::EccP256),
KeyAlgorithmId::EccP384 => Some(AlgorithmId::EccP384),
KeyAlgorithmId::EccP521 => None,
}
}
pub fn is_rsa(&self) -> bool {
match self {
KeyAlgorithmId::Rsa1024 | KeyAlgorithmId::Rsa2048 => true,
KeyAlgorithmId::EccP256 | KeyAlgorithmId::EccP384 | KeyAlgorithmId::EccP521 => false,
}
}
pub fn is_ecc(&self) -> bool {
match self {
KeyAlgorithmId::Rsa1024 | KeyAlgorithmId::Rsa2048 => false,
KeyAlgorithmId::EccP256 | KeyAlgorithmId::EccP384 | KeyAlgorithmId::EccP521 => true,
}
}
}
impl FromStr for KeyAlgorithmId {
fn from_str(s: &str) -> Option<Self>
where
Self: Sized,
{
match s {
"rsa1024" => Some(KeyAlgorithmId::Rsa1024),
"rsa2048" => Some(KeyAlgorithmId::Rsa2048),
"p256" => Some(KeyAlgorithmId::EccP256),
"p384" => Some(KeyAlgorithmId::EccP384),
"p521" => Some(KeyAlgorithmId::EccP521),
_ => None,
}
}
}
impl ToStr for KeyAlgorithmId {
fn to_str(&self) -> &str {
match self {
KeyAlgorithmId::Rsa1024 => "rsa1024",
KeyAlgorithmId::Rsa2048 => "rsa2048",
KeyAlgorithmId::EccP256 => "p256",
KeyAlgorithmId::EccP384 => "p384",
KeyAlgorithmId::EccP521 => "p521",
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum KeyUsage {
Any,
@@ -78,8 +150,9 @@ impl ToString for KeyUsage {
match self {
KeyUsage::Any => "*",
KeyUsage::Singing => "signing",
KeyUsage::KeyAgreement => "key_agreement"
}.to_string()
KeyUsage::KeyAgreement => "key_agreement",
}
.to_string()
}
}
@@ -95,7 +168,7 @@ pub struct SecureEnclaveKey {
#[derive(Debug)]
pub struct YubikeyPivKey {
pub key_name: String,
pub algorithm: AlgorithmId,
pub algorithm: KeyAlgorithmId,
pub slot: SlotId,
}
@@ -103,7 +176,7 @@ pub struct YubikeyPivKey {
#[derive(Debug)]
pub struct YubikeyHmacEncSoftKey {
pub key_name: String,
pub algorithm: AlgorithmId,
pub algorithm: KeyAlgorithmId,
pub hmac_enc_private_key: String,
}
@@ -142,8 +215,16 @@ pub fn parse_key_uri(key_uri: &str) -> XResult<KeyUri> {
if "" != usage {
return simple_error!("Key uri's usage must be empty.");
}
let algorithm = opt_value_result!(AlgorithmId::from_str(algorithm), "Invalid algorithm id: {}", algorithm);
let slot = opt_value_result!(SlotId::from_str(left_part), "Invalid slot id: {}", left_part);
let algorithm = opt_value_result!(
KeyAlgorithmId::from_str(algorithm),
"Invalid algorithm id: {}",
algorithm
);
let slot = opt_value_result!(
SlotId::from_str(left_part),
"Invalid slot id: {}",
left_part
);
let parsed_key_uri = KeyUri::YubikeyPivKey(YubikeyPivKey {
key_name: host_or_name.to_string(),
algorithm,
@@ -156,7 +237,11 @@ pub fn parse_key_uri(key_uri: &str) -> XResult<KeyUri> {
if "" != usage {
return simple_error!("Key uri's usage must be empty.");
}
let algorithm = opt_value_result!(AlgorithmId::from_str(algorithm), "Invalid algorithm id: {}", algorithm);
let algorithm = opt_value_result!(
KeyAlgorithmId::from_str(algorithm),
"Invalid algorithm id: {}",
algorithm
);
let hmac_enc_private_key = left_part.to_string();
let parsed_key_uri = KeyUri::YubikeyHmacEncSoftKey(YubikeyHmacEncSoftKey {
key_name: host_or_name.to_string(),
@@ -174,7 +259,10 @@ pub fn parse_key_uri(key_uri: &str) -> XResult<KeyUri> {
fn test_parse_key_uri_01() {
let se_key_uri =
parse_key_uri("key://hatter-mac-pro:se/p256:signing:BASE64(dataRepresentation)").unwrap();
assert_eq!("key://hatter-mac-pro:se/p256:signing:BASE64(dataRepresentation)", se_key_uri.to_string());
assert_eq!(
"key://hatter-mac-pro:se/p256:signing:BASE64(dataRepresentation)",
se_key_uri.to_string()
);
match se_key_uri {
KeyUri::SecureEnclaveKey(se_key_uri) => {
assert_eq!("hatter-mac-pro", se_key_uri.host);
@@ -192,7 +280,10 @@ fn test_parse_key_uri_02() {
let se_key_uri =
parse_key_uri("key://hatter-mac-m1:se/p256:key_agreement:BASE64(dataRepresentation)")
.unwrap();
assert_eq!("key://hatter-mac-m1:se/p256:key_agreement:BASE64(dataRepresentation)", se_key_uri.to_string());
assert_eq!(
"key://hatter-mac-m1:se/p256:key_agreement:BASE64(dataRepresentation)",
se_key_uri.to_string()
);
match se_key_uri {
KeyUri::SecureEnclaveKey(se_key_uri) => {
assert_eq!("hatter-mac-m1", se_key_uri.host);
@@ -208,11 +299,14 @@ fn test_parse_key_uri_02() {
#[test]
fn test_parse_key_uri_03() {
let se_key_uri = parse_key_uri("key://yubikey-5n:piv/p256::9a").unwrap();
assert_eq!("key://yubikey-5n:piv/p256::authentication", se_key_uri.to_string());
assert_eq!(
"key://yubikey-5n:piv/p256::authentication",
se_key_uri.to_string()
);
match se_key_uri {
KeyUri::YubikeyPivKey(piv_key_uri) => {
assert_eq!("yubikey-5n", piv_key_uri.key_name);
assert_eq!(AlgorithmId::EccP256, piv_key_uri.algorithm);
assert_eq!(KeyAlgorithmId::EccP256, piv_key_uri.algorithm);
assert_eq!(SlotId::Authentication, piv_key_uri.slot);
}
_ => {

View File

@@ -1,15 +1,66 @@
use std::collections::HashMap;
use ecdsa::elliptic_curve::rand_core::OsRng;
use openssl::bn::{BigNum, BigNumContext};
use openssl::pkey::PKey;
use openssl::rsa::{Padding, Rsa};
use rsa::RsaPublicKey;
use rsa::{Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey};
use rust_util::{util_msg, XResult};
use rust_util::util_msg::MessageType;
use spki::DecodePublicKey;
use rsa::pkcs1::DecodeRsaPublicKey;
use rsa::traits::PublicKeyParts;
use spki::EncodePublicKey;
use rsa::pkcs1::LineEnding;
use rsa::pkcs8::EncodePrivateKey;
use sha2::{Sha256, Sha384, Sha512};
use crate::digestutil;
use crate::util::{base64_decode, base64_encode};
pub enum RsaSignAlgorithm {
Rs256,
Rs384,
Rs512,
}
impl RsaSignAlgorithm {
pub fn from_str(alg: &str) -> Option<RsaSignAlgorithm> {
match alg {
"RS256" => Some(RsaSignAlgorithm::Rs256),
"RS384" => Some(RsaSignAlgorithm::Rs384),
"RS512" => Some(RsaSignAlgorithm::Rs512),
_ => None
}
}
}
pub fn sign(rsa_private_key: &RsaPrivateKey, rsa_sign_algorithm: RsaSignAlgorithm, message: &[u8]) -> XResult<Vec<u8>> {
match rsa_sign_algorithm {
RsaSignAlgorithm::Rs256 => {
let raw_in = digestutil::sha256_bytes(&message);
Ok(rsa_private_key.sign(Pkcs1v15Sign::new::<Sha256>(), &raw_in)?)
}
RsaSignAlgorithm::Rs384 => {
let raw_in = digestutil::sha384_bytes(&message);
Ok(rsa_private_key.sign(Pkcs1v15Sign::new::<Sha384>(), &raw_in)?)
}
RsaSignAlgorithm::Rs512 => {
let raw_in = digestutil::sha512_bytes(&message);
Ok(rsa_private_key.sign(Pkcs1v15Sign::new::<Sha512>(), &raw_in)?)
}
}
}
pub fn generate_rsa_keypair(bit_size: usize) -> XResult<(String, String, String, Vec<u8>, String)> {
let rsa_private_key = opt_result!(RsaPrivateKey::new(&mut OsRng, bit_size), "Generate RSA private key failed: {}");
let rsa_public_key = rsa_private_key.to_public_key();
let secret_key_der_base64 = base64_encode(rsa_private_key.to_pkcs8_der()?.as_bytes());
let secret_key_pem = rsa_private_key.to_pkcs8_pem(LineEnding::LF)?.to_string();
let public_key_pem = rsa_public_key.to_public_key_pem(LineEnding::LF)?;
let public_key_der = rsa_public_key.to_public_key_der()?.to_vec();
let jwk_ec_key = rsa_public_key_to_jwk(&rsa_public_key)?;
Ok((secret_key_der_base64, secret_key_pem, public_key_pem, public_key_der, jwk_ec_key))
}
#[derive(Debug)]
pub struct RsaCrt {
// n = p * q
@@ -160,7 +211,10 @@ fn pkcs1_padding_for_sign(bs: &[u8], bit_len: usize) -> XResult<Vec<u8>> {
pub fn convert_rsa_to_jwk(public_key: &str) -> XResult<String> {
let rsa_public_key = try_parse_rsa(public_key)?;
rsa_public_key_to_jwk(&rsa_public_key)
}
pub fn rsa_public_key_to_jwk(rsa_public_key: &RsaPublicKey) -> XResult<String> {
let e_bytes = rsa_public_key.e().to_bytes_be();
let n_bytes = rsa_public_key.n().to_bytes_be();