Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSA Key support #29

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ ouroboros = { version = "0.17" }
p256 = { version = "0.13", features = ["pkcs8", "arithmetic", "jwk"] }
pem-rfc7468 = { version = "0.7", features = ["std"] }
pkcs8 = { version = "0.10", features = ["alloc", "pem"] }
rsa = "0.9"
rsa = { version = "0.9", features = ["sha2"] }
reqwest = { version = "0.11", features = [
"rustls-tls",
"json",
Expand Down
1 change: 1 addition & 0 deletions src/key/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ impl EcdsaSignature {

/// Named elliptic curves supported by Yacme
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum EcdsaAlgorithm {
/// The NIST P-256 (a.k.a. secp256r1, prime256v1) elliptic curve.
P256,
Expand Down
21 changes: 21 additions & 0 deletions src/key/jwk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::fmt;

use base64ct::Encoding;
use elliptic_curve::sec1::Coordinates;
use rsa::traits::PublicKeyParts;
use serde::ser::{self, SerializeStruct};
use sha2::Digest;

Expand Down Expand Up @@ -48,6 +49,7 @@ impl Jwk {
#[derive(Clone, PartialEq, Eq)]
enum InnerJwk {
EllipticCurve(elliptic_curve::JwkEcKey),
Rsa(::rsa::RsaPublicKey),
}

impl ser::Serialize for Jwk {
Expand All @@ -66,6 +68,19 @@ impl ser::Serialize for Jwk {
state.serialize_field("y", &base64ct::Base64UrlUnpadded::encode_string(y))?;
state.end()
}
InnerJwk::Rsa(rsa) => {
let mut state = serializer.serialize_struct("Jwk", 3)?;
state.serialize_field(
"e",
&base64ct::Base64UrlUnpadded::encode_string(&rsa.e().to_bytes_le()),
)?;
state.serialize_field("kty", "RSA")?;
state.serialize_field(
"n",
&base64ct::Base64UrlUnpadded::encode_string(&rsa.n().to_bytes_le()),
)?;
state.end()
}
}
}
}
Expand All @@ -75,3 +90,9 @@ impl From<elliptic_curve::JwkEcKey> for Jwk {
Jwk(InnerJwk::EllipticCurve(value))
}
}

impl From<::rsa::RsaPublicKey> for Jwk {
fn from(value: ::rsa::RsaPublicKey) -> Self {
Jwk(InnerJwk::Rsa(value))
}
}
56 changes: 54 additions & 2 deletions src/key/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ use std::fmt;
pub mod cert;
mod ecdsa;
pub mod jwk;
mod rsa;

use self::ecdsa::EcdsaSigningKey;
use self::{ecdsa::EcdsaSigningKey, rsa::RsaSigningKey};

pub use self::ecdsa::EcdsaAlgorithm;
pub use self::{ecdsa::EcdsaAlgorithm, rsa::RsaAlgorithm};

use ::rsa::pkcs1::DecodeRsaPrivateKey;
pub use p256;
pub use pkcs8;
use pkcs8::EncodePublicKey;
Expand All @@ -27,6 +29,7 @@ pub use signature;
#[derive(Debug)]
enum InnerSignature {
Ecdsa(self::ecdsa::EcdsaSignature),
Rsa(self::rsa::RsaSignature),
}

impl From<self::ecdsa::EcdsaSignature> for Signature {
Expand All @@ -35,16 +38,24 @@ impl From<self::ecdsa::EcdsaSignature> for Signature {
}
}

impl From<self::rsa::RsaSignature> for Signature {
fn from(value: self::rsa::RsaSignature) -> Self {
Signature(InnerSignature::Rsa(value))
}
}

impl InnerSignature {
fn to_der(&self) -> der::Document {
match self {
InnerSignature::Ecdsa(sig) => sig.to_der(),
InnerSignature::Rsa(sig) => sig.to_der(),
}
}

fn to_bytes(&self) -> Box<[u8]> {
match self {
InnerSignature::Ecdsa(sig) => sig.to_bytes(),
InnerSignature::Rsa(sig) => sig.to_bytes(),
}
}
}
Expand Down Expand Up @@ -146,16 +157,21 @@ impl EncodePublicKey for PublicKey {
/// let algorithm = SignatureKind::Ecdsa(EcdsaAlgorithm::P256);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SignatureKind {
/// ECDSA using a variety of potential curves
Ecdsa(EcdsaAlgorithm),

/// RSA Signing Key
RSA(RsaAlgorithm),
}

impl SignatureKind {
/// Create a new, secure random signing key for this signature kind.
pub fn random(&self) -> SigningKey {
match self {
SignatureKind::Ecdsa(ecdsa) => SigningKey(ecdsa.random().into()),
SignatureKind::RSA(rsa) => SigningKey(rsa.random().into()),
}
}
}
Expand All @@ -182,6 +198,23 @@ impl SignatureKind {
pub struct SigningKey(InnerSigningKey);

impl SigningKey {
/// Read an RSA private key from a PEM-encoded PKCS#1 format key (usually the kind produced by OpenSSL)
/// into the current signing key document. You must provide the signature kind, as the code
/// does not currently infer the type of key in use.
pub fn from_pkcs1_pem(
data: &str,
signature: SignatureKind,
) -> Result<Self, ::rsa::pkcs1::Error> {
match signature {
SignatureKind::Ecdsa(_) => panic!("ECDSA not supported by PKCS1"),
SignatureKind::RSA(alg) => {
let keypair = ::rsa::RsaPrivateKey::from_pkcs1_pem(data)?;
let signing_key = self::rsa::RsaSigningKey::new(alg, keypair);
Ok(SigningKey(signing_key.into()))
}
}
}

/// Read a private key from a PEM-encoded PKCS#8 format key (usually the kind produced by
/// OpenSSL) into this signing key document. You must provide the signature kind, as the code
/// does not currently infer the type of key in use.
Expand All @@ -190,13 +223,17 @@ impl SigningKey {
SignatureKind::Ecdsa(algorithm) => Ok(SigningKey(InnerSigningKey::Ecdsa(Box::new(
EcdsaSigningKey::from_pkcs8_pem(data, algorithm)?,
)))),
SignatureKind::RSA(algorithm) => Ok(SigningKey(InnerSigningKey::Rsa(Box::new(
RsaSigningKey::from_pkcs8_pem(data, algorithm)?,
)))),
}
}

/// Return the signature kind, used to recover the key type from a PEM file.
pub fn kind(&self) -> SignatureKind {
match &self.0 {
InnerSigningKey::Ecdsa(key) => key.kind(),
InnerSigningKey::Rsa(key) => key.kind(),
}
}
}
Expand Down Expand Up @@ -250,25 +287,29 @@ impl signature::RandomizedDigestSigner<sha2::Sha256, Signature> for SigningKey {

pub(crate) enum InnerSigningKey {
Ecdsa(Box<dyn SigningKeyAlgorithm + Send + Sync>),
Rsa(Box<dyn SigningKeyAlgorithm + Send + Sync>),
//TODO: Consider supporting other algorithms?
}

impl InnerSigningKey {
pub(crate) fn as_jwk(&self) -> self::jwk::Jwk {
match self {
InnerSigningKey::Ecdsa(ecdsa) => ecdsa.as_jwk(),
InnerSigningKey::Rsa(rsa) => rsa.as_jwk(),
}
}

pub(crate) fn public_key(&self) -> PublicKey {
match self {
InnerSigningKey::Ecdsa(ecdsa) => ecdsa.public_key(),
InnerSigningKey::Rsa(rsa) => rsa.public_key(),
}
}

pub(crate) fn algorithm(&self) -> spki::AlgorithmIdentifierOwned {
match self {
InnerSigningKey::Ecdsa(ecdsa) => ecdsa.algorithm(),
InnerSigningKey::Rsa(rsa) => rsa.algorithm(),
}
}
}
Expand All @@ -279,6 +320,12 @@ impl From<EcdsaSigningKey> for InnerSigningKey {
}
}

impl From<RsaSigningKey> for InnerSigningKey {
fn from(value: RsaSigningKey) -> Self {
InnerSigningKey::Rsa(Box::new(value) as _)
}
}

impl PartialEq for InnerSigningKey {
fn eq(&self, other: &Self) -> bool {
self.public_key() == other.public_key()
Expand All @@ -291,6 +338,7 @@ impl pkcs8::EncodePrivateKey for InnerSigningKey {
fn to_pkcs8_der(&self) -> pkcs8::Result<der::SecretDocument> {
match self {
InnerSigningKey::Ecdsa(key) => key.to_pkcs8_der(),
InnerSigningKey::Rsa(key) => key.to_pkcs8_der(),
}
}
}
Expand All @@ -299,6 +347,7 @@ impl signature::Signer<Signature> for InnerSigningKey {
fn try_sign(&self, msg: &[u8]) -> Result<Signature, ::ecdsa::Error> {
match self {
InnerSigningKey::Ecdsa(key) => key.try_sign(msg),
InnerSigningKey::Rsa(key) => key.try_sign(msg),
}
}
}
Expand All @@ -307,6 +356,7 @@ impl signature::DigestSigner<sha2::Sha256, Signature> for InnerSigningKey {
fn try_sign_digest(&self, digest: sha2::Sha256) -> Result<Signature, ::ecdsa::Error> {
match self {
InnerSigningKey::Ecdsa(key) => key.try_sign_digest(digest),
InnerSigningKey::Rsa(key) => key.try_sign_digest(digest),
}
}
}
Expand All @@ -318,6 +368,7 @@ impl signature::RandomizedDigestSigner<sha2::Sha256, Signature> for InnerSigning
) -> Result<Signature, ::ecdsa::Error> {
match self {
InnerSigningKey::Ecdsa(key) => key.try_sign_digest_with_rng(digest),
InnerSigningKey::Rsa(key) => key.try_sign_digest_with_rng(digest),
}
}
}
Expand All @@ -326,6 +377,7 @@ impl fmt::Debug for InnerSigningKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InnerSigningKey::Ecdsa(_) => f.write_str("ECDSA-Key"),
InnerSigningKey::Rsa(_) => f.write_str("RSA-Key"),
}
}
}
Expand Down
Loading
Loading