Skip to content

Commit

Permalink
Enable signing without private key
Browse files Browse the repository at this point in the history
  • Loading branch information
kwantam authored and djc committed Sep 24, 2024
1 parent b3ce6ed commit 5f6b261
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 4 deletions.
7 changes: 4 additions & 3 deletions rcgen/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl CertificateParams {
/// [`Certificate::pem`].
pub fn signed_by(
self,
key_pair: &KeyPair,
public_key: &impl PublicKeyData,
issuer: &Certificate,
issuer_key: &KeyPair,
) -> Result<Certificate, Error> {
Expand All @@ -160,8 +160,9 @@ impl CertificateParams {
key_pair: issuer_key,
};

let subject_public_key_info = key_pair.public_key_der();
let der = self.serialize_der_with_signer(key_pair, issuer)?;
let subject_public_key_info =
yasna::construct_der(|writer| serialize_public_key_der(public_key, writer));
let der = self.serialize_der_with_signer(public_key, issuer)?;
Ok(Certificate {
params: self,
subject_public_key_info,
Expand Down
5 changes: 5 additions & 0 deletions rcgen/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ pub enum Error {
#[cfg(not(feature = "crypto"))]
/// Missing serial number
MissingSerialNumber,
/// X509 parsing error
#[cfg(feature = "x509-parser")]
X509(String),
}

impl fmt::Display for Error {
Expand Down Expand Up @@ -91,6 +94,8 @@ impl fmt::Display for Error {
)?,
#[cfg(not(feature = "crypto"))]
MissingSerialNumber => write!(f, "A serial number must be specified")?,
#[cfg(feature = "x509-parser")]
X509(e) => write!(f, "X509 error: {e}")?,
};
Ok(())
}
Expand Down
84 changes: 84 additions & 0 deletions rcgen/src/key_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,69 @@ impl<T> ExternalError<T> for Result<T, pem::PemError> {
}
}

/// A public key
#[derive(Debug)]
pub struct SubjectPublicKeyInfo {
pub(crate) alg: &'static SignatureAlgorithm,
pub(crate) subject_public_key: Vec<u8>,
}

impl SubjectPublicKeyInfo {
/// Create a `SubjectPublicKey` value from a PEM-encoded SubjectPublicKeyInfo string
#[cfg(all(feature = "x509-parser", feature = "pem"))]
pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
let spki_der = pem::parse(pem_str)._err()?.into_contents();
Self::from_der(&spki_der)
}

/// Create a `SubjectPublicKey` value from DER-encoded SubjectPublicKeyInfo bytes
#[cfg(feature = "x509-parser")]
pub fn from_der(spki_der: &[u8]) -> Result<Self, Error> {
use x509_parser::{
prelude::FromDer,
x509::{AlgorithmIdentifier, SubjectPublicKeyInfo},
};

let (rem, spki) =
SubjectPublicKeyInfo::from_der(spki_der).map_err(|e| Error::X509(e.to_string()))?;
if !rem.is_empty() {
return Err(Error::X509(
"trailing bytes in SubjectPublicKeyInfo".to_string(),
));
}

let alg = SignatureAlgorithm::iter()
.find(|alg| {
let bytes = yasna::construct_der(|writer| {
alg.write_oids_sign_alg(writer);
});
let Ok((rest, aid)) = AlgorithmIdentifier::from_der(&bytes) else {
return false;
};
if !rest.is_empty() {
return false;
}
aid == spki.algorithm
})
.ok_or(Error::UnsupportedSignatureAlgorithm)?;

Ok(Self {
alg,
subject_public_key: Vec::from(spki.subject_public_key.as_ref()),
})
}
}

impl PublicKeyData for SubjectPublicKeyInfo {
fn der_bytes(&self) -> &[u8] {
&self.subject_public_key
}

fn algorithm(&self) -> &SignatureAlgorithm {
self.alg
}
}

/// The public key data of a key pair
pub trait PublicKeyData {
/// The public key in DER format
Expand Down Expand Up @@ -725,4 +788,25 @@ mod test {
let key_pair = KeyPair::try_from(der).unwrap();
assert_eq!(key_pair.algorithm(), &PKCS_ECDSA_P256_SHA256);
}

#[cfg(all(feature = "x509-parser", feature = "pem"))]
#[test]
fn test_subject_public_key_parsing() {
// NOTE: the other algorithms supported by this crate don't support keygen
for alg in [
&PKCS_ED25519,
&PKCS_ECDSA_P256_SHA256,
&PKCS_ECDSA_P384_SHA384,
] {
let kp = KeyPair::generate_for(alg).expect("keygen");
let pem = kp.public_key_pem();
let der = kp.public_key_der();

let pkd_pem = SubjectPublicKeyInfo::from_pem(&pem).expect("from pem");
assert_eq!(kp.der_bytes(), pkd_pem.der_bytes());

let pkd_der = SubjectPublicKeyInfo::from_der(&der).expect("from der");
assert_eq!(kp.der_bytes(), pkd_der.der_bytes());
}
}
}
2 changes: 1 addition & 1 deletion rcgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub use error::{Error, InvalidAsn1String};
pub use key_pair::PublicKeyData;
#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))]
pub use key_pair::RsaKeySize;
pub use key_pair::{KeyPair, RemoteKeyPair};
pub use key_pair::{KeyPair, RemoteKeyPair, SubjectPublicKeyInfo};
#[cfg(feature = "crypto")]
use ring_like::digest;
pub use sign_algo::algo::*;
Expand Down

0 comments on commit 5f6b261

Please sign in to comment.