feat: v1.12.6
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -508,7 +508,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "card-cli"
|
name = "card-cli"
|
||||||
version = "1.12.5"
|
version = "1.12.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm-stream",
|
"aes-gcm-stream",
|
||||||
"authenticator 0.3.1",
|
"authenticator 0.3.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "card-cli"
|
name = "card-cli"
|
||||||
version = "1.12.5"
|
version = "1.12.6"
|
||||||
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
|||||||
26
examples/rsa.rs
Normal file
26
examples/rsa.rs
Normal 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()));
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::keyutil::{parse_key_uri, KeyUri, KeyUsage};
|
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::yubikeyutil::find_key_or_error;
|
||||||
use crate::{cmdutil, ecdsautil, hmacutil, seutil, util, yubikeyutil};
|
use crate::{cmdutil, ecdsautil, hmacutil, seutil, util, yubikeyutil};
|
||||||
use clap::{App, ArgMatches, SubCommand};
|
use clap::{App, ArgMatches, SubCommand};
|
||||||
@@ -8,7 +8,10 @@ use rust_util::util_clap::{Command, CommandError};
|
|||||||
use rust_util::XResult;
|
use rust_util::XResult;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use rsa::RsaPrivateKey;
|
||||||
|
use spki::EncodePublicKey;
|
||||||
use x509_parser::parse_x509_certificate;
|
use x509_parser::parse_x509_certificate;
|
||||||
|
use crate::pivutil::ToStr;
|
||||||
|
|
||||||
pub struct CommandImpl;
|
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)
|
simple_error!("Slot {} not found", key.slot)
|
||||||
}
|
}
|
||||||
KeyUri::YubikeyHmacEncSoftKey(key) => {
|
KeyUri::YubikeyHmacEncSoftKey(key) => {
|
||||||
let private_key = hmacutil::try_hmac_decrypt_to_string(&key.hmac_enc_private_key)?;
|
if key.algorithm.is_ecc() {
|
||||||
let p256_public_key = ecdsautil::parse_p256_private_key_to_public_key(&private_key).ok();
|
let private_key = hmacutil::try_hmac_decrypt_to_string(&key.hmac_enc_private_key)?;
|
||||||
let p384_public_key = ecdsautil::parse_p384_private_key_to_public_key(&private_key).ok();
|
let p256_public_key = ecdsautil::parse_p256_private_key_to_public_key(&private_key).ok();
|
||||||
let p521_public_key = ecdsautil::parse_p521_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 {
|
if let Some(p256_public_key) = p256_public_key {
|
||||||
return Ok(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")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
use crate::cmd_sign_jwt::digest_by_jwt_algorithm;
|
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::pivutil::ToStr;
|
||||||
|
use crate::rsautil::RsaSignAlgorithm;
|
||||||
use crate::util::{base64_decode, base64_encode};
|
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 clap::{App, ArgMatches, SubCommand};
|
||||||
use jwt::AlgorithmType;
|
use jwt::AlgorithmType;
|
||||||
|
use rsa::RsaPrivateKey;
|
||||||
use rust_util::util_clap::{Command, CommandError};
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
use rust_util::XResult;
|
use rust_util::XResult;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use yubikey::piv::{sign_data, AlgorithmId};
|
use yubikey::piv::sign_data;
|
||||||
use crate::cmd_sign_jwt_soft::{convert_jwt_algorithm_to_ecdsa_algorithm, parse_ecdsa_private_key};
|
|
||||||
use crate::ecdsautil::EcdsaSignType;
|
|
||||||
|
|
||||||
pub struct CommandImpl;
|
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 raw_in = digest_by_jwt_algorithm(jwt_algorithm, &message_bytes)?;
|
||||||
let signed_data = opt_result!(
|
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: {}"
|
"Sign YubiKey failed: {}"
|
||||||
);
|
);
|
||||||
Ok(signed_data.to_vec())
|
Ok(signed_data.to_vec())
|
||||||
}
|
}
|
||||||
KeyUri::YubikeyHmacEncSoftKey(key) => {
|
KeyUri::YubikeyHmacEncSoftKey(key) => {
|
||||||
let private_key = hmacutil::try_hmac_decrypt_to_string(&key.hmac_enc_private_key)?;
|
if key.algorithm.is_ecc() {
|
||||||
let (jwt_algorithm, private_key_d) = parse_ecdsa_private_key(&private_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)?;
|
||||||
|
|
||||||
let raw_in = digest_by_jwt_algorithm(jwt_algorithm, &message_bytes)?;
|
let raw_in = digest_by_jwt_algorithm(jwt_algorithm, &message_bytes)?;
|
||||||
let ecdsa_algorithm = convert_jwt_algorithm_to_ecdsa_algorithm(jwt_algorithm)?;
|
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 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 {
|
let jwt_algorithm = match alg {
|
||||||
"ES256" => AlgorithmType::Es256,
|
"ES256" => AlgorithmType::Es256,
|
||||||
"ES384" => AlgorithmType::Es384,
|
"ES384" => AlgorithmType::Es384,
|
||||||
|
"ES512" => AlgorithmType::Es512,
|
||||||
"RS256" => AlgorithmType::Rs256,
|
"RS256" => AlgorithmType::Rs256,
|
||||||
_ => return simple_error!("Invalid alg: {}", alg),
|
_ => return simple_error!("Invalid alg: {}", alg),
|
||||||
};
|
};
|
||||||
if key.algorithm == AlgorithmId::Rsa1024 {
|
if key.algorithm == KeyAlgorithmId::Rsa1024 {
|
||||||
return simple_error!("Invalid algorithm: RSA1024");
|
return simple_error!("Invalid algorithm: RSA1024");
|
||||||
}
|
}
|
||||||
let is_p256_mismatch =
|
let is_p256_mismatch =
|
||||||
key.algorithm == AlgorithmId::EccP256 && jwt_algorithm != AlgorithmType::Es256;
|
key.algorithm == KeyAlgorithmId::EccP256 && jwt_algorithm != AlgorithmType::Es256;
|
||||||
let is_p384_mismatch =
|
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 =
|
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);
|
return simple_error!("Invalid algorithm: {} vs {}", key.algorithm.to_str(), alg);
|
||||||
}
|
}
|
||||||
Ok(jwt_algorithm)
|
Ok(jwt_algorithm)
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
|
use crate::ecdsautil::EcdsaAlgorithm;
|
||||||
use crate::keychain::{KeychainKey, KeychainKeyValue};
|
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 clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
use rust_util::util_clap::{Command, CommandError};
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
use std::collections::BTreeMap;
|
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;
|
pub struct CommandImpl;
|
||||||
|
|
||||||
@@ -23,7 +22,7 @@ impl Command for CommandImpl {
|
|||||||
.long("type")
|
.long("type")
|
||||||
.required(true)
|
.required(true)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("Key type (e.g. p256, p384, p521)"),
|
.help("Key type (e.g. p256, p384, p521, rsa1024, rsa2048)"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("with-hmac-encrypt")
|
Arg::with_name("with-hmac-encrypt")
|
||||||
@@ -49,20 +48,26 @@ impl Command for CommandImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ecdsa_algorithm = match key_type.as_str() {
|
let ecdsa_algorithm = match key_type.as_str() {
|
||||||
"p256" => EcdsaAlgorithm::P256,
|
"p256" => Some(EcdsaAlgorithm::P256),
|
||||||
"p384" => EcdsaAlgorithm::P384,
|
"p384" => Some(EcdsaAlgorithm::P384),
|
||||||
"p521" => EcdsaAlgorithm::P521,
|
"p521" => Some(EcdsaAlgorithm::P521),
|
||||||
_ => {
|
_ => None,
|
||||||
return simple_error!("Key type must be p256 or p384");
|
};
|
||||||
}
|
let rsa_bit_size: Option<usize> = match key_type.as_str() {
|
||||||
|
"rsa1024" => Some(1024),
|
||||||
|
"rsa2048" => Some(2048),
|
||||||
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (pkcs8_base64,
|
let (
|
||||||
secret_key_pem,
|
pkcs8_base64, secret_key_pem, public_key_pem, public_key_der, jwk_key
|
||||||
public_key_pem,
|
) = if let Some(ecdsa_algorithm) = ecdsa_algorithm {
|
||||||
public_key_der,
|
ecdsautil::generate_ecdsa_keypair(ecdsa_algorithm)?
|
||||||
jwk_ec_key
|
} else if let Some(rsa_bit_size) = rsa_bit_size {
|
||||||
) = ecdsautil::generate_ecdsa_keypair(ecdsa_algorithm)?;
|
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 {
|
let (pkcs8_base64, secret_key_pem) = if with_hmac_encrypt {
|
||||||
(
|
(
|
||||||
@@ -80,7 +85,7 @@ impl Command for CommandImpl {
|
|||||||
pkcs8_base64: pkcs8_base64.clone(),
|
pkcs8_base64: pkcs8_base64.clone(),
|
||||||
secret_key_pem: secret_key_pem.clone(),
|
secret_key_pem: secret_key_pem.clone(),
|
||||||
public_key_pem: public_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)?;
|
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_base64", pkcs8_base64.clone());
|
||||||
json.insert("private_key_pem", secret_key_pem);
|
json.insert("private_key_pem", secret_key_pem);
|
||||||
let algorithm_id = match key_type.as_str() {
|
let algorithm_id = match key_type.as_str() {
|
||||||
"p256" => Some(AlgorithmId::EccP256),
|
"p256" => Some(KeyAlgorithmId::EccP256),
|
||||||
"p384" => Some(AlgorithmId::EccP384),
|
"p384" => Some(KeyAlgorithmId::EccP384),
|
||||||
|
"p521" => Some(KeyAlgorithmId::EccP521),
|
||||||
|
"rsa1024" => Some(KeyAlgorithmId::Rsa1024),
|
||||||
|
"rsa2048" => Some(KeyAlgorithmId::Rsa2048),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let (true, Some(algorithm_id)) = (with_hmac_encrypt, algorithm_id) {
|
if let (true, Some(algorithm_id)) = (with_hmac_encrypt, algorithm_id) {
|
||||||
let yk = yubikeyutil::open_yubikey()?;
|
let yk = yubikeyutil::open_yubikey()?;
|
||||||
let yubikey_hmac_enc_soft_key = YubikeyHmacEncSoftKey {
|
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,
|
algorithm: algorithm_id,
|
||||||
hmac_enc_private_key: pkcs8_base64,
|
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) => {
|
Some(keychain_key_uri) => {
|
||||||
@@ -118,7 +129,7 @@ impl Command for CommandImpl {
|
|||||||
}
|
}
|
||||||
json.insert("public_key_pem", public_key_pem);
|
json.insert("public_key_pem", public_key_pem);
|
||||||
json.insert("public_key_base64", public_key_base64);
|
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);
|
util::print_pretty_json(&json);
|
||||||
} else {
|
} else {
|
||||||
@@ -133,7 +144,7 @@ impl Command for CommandImpl {
|
|||||||
}
|
}
|
||||||
information!("Public key PEM:\n{}", public_key_pem);
|
information!("Public key PEM:\n{}", public_key_pem);
|
||||||
information!("Public key Base64:\n{}\n", public_key_base64);
|
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)
|
Ok(None)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use yubikey::Key;
|
|||||||
use yubikey::piv::{AlgorithmId, metadata};
|
use yubikey::piv::{AlgorithmId, metadata};
|
||||||
|
|
||||||
use crate::{cmdutil, pivutil, util, yubikeyutil};
|
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::pivutil::{get_algorithm_id_by_certificate, slot_equals, ToStr};
|
||||||
use crate::pkiutil::bytes_to_pem;
|
use crate::pkiutil::bytes_to_pem;
|
||||||
use crate::sshutil::SshVecWriter;
|
use crate::sshutil::SshVecWriter;
|
||||||
@@ -106,7 +106,7 @@ impl Command for CommandImpl {
|
|||||||
|
|
||||||
let yubikey_piv_key = YubikeyPivKey {
|
let yubikey_piv_key = YubikeyPivKey {
|
||||||
key_name: format!("yubikey{}-{}", yk.version().major, yk.serial().0),
|
key_name: format!("yubikey{}-{}", yk.version().major, yk.serial().0),
|
||||||
algorithm: algorithm_id,
|
algorithm: KeyAlgorithmId::from_algorithm_id(algorithm_id),
|
||||||
slot: slot_id,
|
slot: slot_id,
|
||||||
};
|
};
|
||||||
json.insert("key_uri", KeyUri::YubikeyPivKey(yubikey_piv_key).to_string());
|
json.insert("key_uri", KeyUri::YubikeyPivKey(yubikey_piv_key).to_string());
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ pub fn digest_by_jwt_algorithm(jwt_algorithm: AlgorithmType, tobe_signed: &[u8])
|
|||||||
}
|
}
|
||||||
AlgorithmType::Es256 => digestutil::sha256_bytes(tobe_signed),
|
AlgorithmType::Es256 => digestutil::sha256_bytes(tobe_signed),
|
||||||
AlgorithmType::Es384 => digestutil::sha384_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),
|
_ => return simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use ecdsa::elliptic_curve::pkcs8::LineEnding;
|
|||||||
use ecdsa::VerifyingKey;
|
use ecdsa::VerifyingKey;
|
||||||
use p256::NistP256;
|
use p256::NistP256;
|
||||||
use p256::ecdsa::signature::hazmat::PrehashVerifier;
|
use p256::ecdsa::signature::hazmat::PrehashVerifier;
|
||||||
use p256::elliptic_curve::JwkEcKey;
|
|
||||||
use p384::NistP384;
|
use p384::NistP384;
|
||||||
use p256::pkcs8::EncodePrivateKey;
|
use p256::pkcs8::EncodePrivateKey;
|
||||||
use p521::NistP521;
|
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 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_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 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))
|
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 {
|
match algo {
|
||||||
EcdsaAlgorithm::P256 => generate_inner_ecdsa_keypair!(p256),
|
EcdsaAlgorithm::P256 => generate_inner_ecdsa_keypair!(p256),
|
||||||
EcdsaAlgorithm::P384 => generate_inner_ecdsa_keypair!(p384),
|
EcdsaAlgorithm::P384 => generate_inner_ecdsa_keypair!(p384),
|
||||||
|
|||||||
118
src/keyutil.rs
118
src/keyutil.rs
@@ -1,7 +1,7 @@
|
|||||||
|
use crate::pivutil::{FromStr, ToStr};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rust_util::XResult;
|
use rust_util::XResult;
|
||||||
use yubikey::piv::{AlgorithmId, SlotId};
|
use yubikey::piv::{AlgorithmId, SlotId};
|
||||||
use crate::pivutil::{ToStr, FromStr};
|
|
||||||
|
|
||||||
// reference: https://git.hatter.ink/hatter/card-cli/issues/6
|
// reference: https://git.hatter.ink/hatter/card-cli/issues/6
|
||||||
#[derive(Debug)]
|
#[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)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum KeyUsage {
|
pub enum KeyUsage {
|
||||||
Any,
|
Any,
|
||||||
@@ -78,8 +150,9 @@ impl ToString for KeyUsage {
|
|||||||
match self {
|
match self {
|
||||||
KeyUsage::Any => "*",
|
KeyUsage::Any => "*",
|
||||||
KeyUsage::Singing => "signing",
|
KeyUsage::Singing => "signing",
|
||||||
KeyUsage::KeyAgreement => "key_agreement"
|
KeyUsage::KeyAgreement => "key_agreement",
|
||||||
}.to_string()
|
}
|
||||||
|
.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +168,7 @@ pub struct SecureEnclaveKey {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct YubikeyPivKey {
|
pub struct YubikeyPivKey {
|
||||||
pub key_name: String,
|
pub key_name: String,
|
||||||
pub algorithm: AlgorithmId,
|
pub algorithm: KeyAlgorithmId,
|
||||||
pub slot: SlotId,
|
pub slot: SlotId,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +176,7 @@ pub struct YubikeyPivKey {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct YubikeyHmacEncSoftKey {
|
pub struct YubikeyHmacEncSoftKey {
|
||||||
pub key_name: String,
|
pub key_name: String,
|
||||||
pub algorithm: AlgorithmId,
|
pub algorithm: KeyAlgorithmId,
|
||||||
pub hmac_enc_private_key: String,
|
pub hmac_enc_private_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,8 +215,16 @@ pub fn parse_key_uri(key_uri: &str) -> XResult<KeyUri> {
|
|||||||
if "" != usage {
|
if "" != usage {
|
||||||
return simple_error!("Key uri's usage must be empty.");
|
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!(
|
||||||
let slot = opt_value_result!(SlotId::from_str(left_part), "Invalid slot id: {}", left_part);
|
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 {
|
let parsed_key_uri = KeyUri::YubikeyPivKey(YubikeyPivKey {
|
||||||
key_name: host_or_name.to_string(),
|
key_name: host_or_name.to_string(),
|
||||||
algorithm,
|
algorithm,
|
||||||
@@ -156,7 +237,11 @@ pub fn parse_key_uri(key_uri: &str) -> XResult<KeyUri> {
|
|||||||
if "" != usage {
|
if "" != usage {
|
||||||
return simple_error!("Key uri's usage must be empty.");
|
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 hmac_enc_private_key = left_part.to_string();
|
||||||
let parsed_key_uri = KeyUri::YubikeyHmacEncSoftKey(YubikeyHmacEncSoftKey {
|
let parsed_key_uri = KeyUri::YubikeyHmacEncSoftKey(YubikeyHmacEncSoftKey {
|
||||||
key_name: host_or_name.to_string(),
|
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() {
|
fn test_parse_key_uri_01() {
|
||||||
let se_key_uri =
|
let se_key_uri =
|
||||||
parse_key_uri("key://hatter-mac-pro:se/p256:signing:BASE64(dataRepresentation)").unwrap();
|
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 {
|
match se_key_uri {
|
||||||
KeyUri::SecureEnclaveKey(se_key_uri) => {
|
KeyUri::SecureEnclaveKey(se_key_uri) => {
|
||||||
assert_eq!("hatter-mac-pro", se_key_uri.host);
|
assert_eq!("hatter-mac-pro", se_key_uri.host);
|
||||||
@@ -192,7 +280,10 @@ fn test_parse_key_uri_02() {
|
|||||||
let se_key_uri =
|
let se_key_uri =
|
||||||
parse_key_uri("key://hatter-mac-m1:se/p256:key_agreement:BASE64(dataRepresentation)")
|
parse_key_uri("key://hatter-mac-m1:se/p256:key_agreement:BASE64(dataRepresentation)")
|
||||||
.unwrap();
|
.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 {
|
match se_key_uri {
|
||||||
KeyUri::SecureEnclaveKey(se_key_uri) => {
|
KeyUri::SecureEnclaveKey(se_key_uri) => {
|
||||||
assert_eq!("hatter-mac-m1", se_key_uri.host);
|
assert_eq!("hatter-mac-m1", se_key_uri.host);
|
||||||
@@ -208,11 +299,14 @@ fn test_parse_key_uri_02() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_key_uri_03() {
|
fn test_parse_key_uri_03() {
|
||||||
let se_key_uri = parse_key_uri("key://yubikey-5n:piv/p256::9a").unwrap();
|
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 {
|
match se_key_uri {
|
||||||
KeyUri::YubikeyPivKey(piv_key_uri) => {
|
KeyUri::YubikeyPivKey(piv_key_uri) => {
|
||||||
assert_eq!("yubikey-5n", piv_key_uri.key_name);
|
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);
|
assert_eq!(SlotId::Authentication, piv_key_uri.slot);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
@@ -1,15 +1,66 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use ecdsa::elliptic_curve::rand_core::OsRng;
|
||||||
use openssl::bn::{BigNum, BigNumContext};
|
use openssl::bn::{BigNum, BigNumContext};
|
||||||
use openssl::pkey::PKey;
|
use openssl::pkey::PKey;
|
||||||
use openssl::rsa::{Padding, Rsa};
|
use openssl::rsa::{Padding, Rsa};
|
||||||
use rsa::RsaPublicKey;
|
use rsa::{Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey};
|
||||||
use rust_util::{util_msg, XResult};
|
use rust_util::{util_msg, XResult};
|
||||||
use rust_util::util_msg::MessageType;
|
use rust_util::util_msg::MessageType;
|
||||||
use spki::DecodePublicKey;
|
use spki::DecodePublicKey;
|
||||||
use rsa::pkcs1::DecodeRsaPublicKey;
|
use rsa::pkcs1::DecodeRsaPublicKey;
|
||||||
use rsa::traits::PublicKeyParts;
|
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};
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct RsaCrt {
|
pub struct RsaCrt {
|
||||||
// n = p * q
|
// 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> {
|
pub fn convert_rsa_to_jwk(public_key: &str) -> XResult<String> {
|
||||||
let rsa_public_key = try_parse_rsa(public_key)?;
|
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 e_bytes = rsa_public_key.e().to_bytes_be();
|
||||||
let n_bytes = rsa_public_key.n().to_bytes_be();
|
let n_bytes = rsa_public_key.n().to_bytes_be();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user