Skip to content

Commit 2faf2e1

Browse files
kwantamdjc
authored andcommitted
Add SubjectPublicKeyInfo type
1 parent fb10501 commit 2faf2e1

File tree

3 files changed

+92
-1
lines changed

3 files changed

+92
-1
lines changed

rcgen/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ pub enum Error {
4545
#[cfg(not(feature = "crypto"))]
4646
/// Missing serial number
4747
MissingSerialNumber,
48+
/// X509 parsing error
49+
#[cfg(feature = "x509-parser")]
50+
X509(String),
4851
}
4952

5053
impl fmt::Display for Error {
@@ -91,6 +94,8 @@ impl fmt::Display for Error {
9194
)?,
9295
#[cfg(not(feature = "crypto"))]
9396
MissingSerialNumber => write!(f, "A serial number must be specified")?,
97+
#[cfg(feature = "x509-parser")]
98+
X509(e) => write!(f, "X.509 parsing error: {e}")?,
9499
};
95100
Ok(())
96101
}

rcgen/src/key_pair.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,68 @@ impl<T> ExternalError<T> for Result<T, pem::PemError> {
690690
}
691691
}
692692

693+
/// A public key
694+
#[derive(Clone, Debug, Eq, PartialEq)]
695+
pub struct SubjectPublicKeyInfo {
696+
pub(crate) alg: &'static SignatureAlgorithm,
697+
pub(crate) subject_public_key: Vec<u8>,
698+
}
699+
700+
impl SubjectPublicKeyInfo {
701+
/// Create a `SubjectPublicKey` value from a PEM-encoded SubjectPublicKeyInfo string
702+
#[cfg(all(feature = "x509-parser", feature = "pem"))]
703+
pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
704+
Self::from_der(&pem::parse(pem_str)._err()?.into_contents())
705+
}
706+
707+
/// Create a `SubjectPublicKey` value from DER-encoded SubjectPublicKeyInfo bytes
708+
#[cfg(feature = "x509-parser")]
709+
pub fn from_der(spki_der: &[u8]) -> Result<Self, Error> {
710+
use x509_parser::{
711+
prelude::FromDer,
712+
x509::{AlgorithmIdentifier, SubjectPublicKeyInfo},
713+
};
714+
715+
let (rem, spki) =
716+
SubjectPublicKeyInfo::from_der(spki_der).map_err(|e| Error::X509(e.to_string()))?;
717+
if !rem.is_empty() {
718+
return Err(Error::X509(
719+
"trailing bytes in SubjectPublicKeyInfo".to_string(),
720+
));
721+
}
722+
723+
let alg = SignatureAlgorithm::iter()
724+
.find(|alg| {
725+
let bytes = yasna::construct_der(|writer| {
726+
alg.write_oids_sign_alg(writer);
727+
});
728+
let Ok((rest, aid)) = AlgorithmIdentifier::from_der(&bytes) else {
729+
return false;
730+
};
731+
if !rest.is_empty() {
732+
return false;
733+
}
734+
aid == spki.algorithm
735+
})
736+
.ok_or(Error::UnsupportedSignatureAlgorithm)?;
737+
738+
Ok(Self {
739+
alg,
740+
subject_public_key: Vec::from(spki.subject_public_key.as_ref()),
741+
})
742+
}
743+
}
744+
745+
impl PublicKeyData for SubjectPublicKeyInfo {
746+
fn der_bytes(&self) -> &[u8] {
747+
&self.subject_public_key
748+
}
749+
750+
fn algorithm(&self) -> &SignatureAlgorithm {
751+
self.alg
752+
}
753+
}
754+
693755
/// The public key data of a key pair
694756
pub trait PublicKeyData {
695757
/// The public key in DER format
@@ -716,6 +778,30 @@ mod test {
716778
signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING},
717779
};
718780

781+
#[cfg(all(feature = "x509-parser", feature = "pem"))]
782+
#[test]
783+
fn test_subject_public_key_parsing() {
784+
for alg in [
785+
&PKCS_ED25519,
786+
&PKCS_ECDSA_P256_SHA256,
787+
&PKCS_ECDSA_P384_SHA384,
788+
#[cfg(feature = "aws_lc_rs")]
789+
&PKCS_ECDSA_P521_SHA512,
790+
#[cfg(feature = "aws_lc_rs")]
791+
&PKCS_RSA_SHA256,
792+
] {
793+
let kp = KeyPair::generate_for(alg).expect("keygen");
794+
let pem = kp.public_key_pem();
795+
let der = kp.public_key_der();
796+
797+
let pkd_pem = SubjectPublicKeyInfo::from_pem(&pem).expect("from pem");
798+
assert_eq!(kp.der_bytes(), pkd_pem.der_bytes());
799+
800+
let pkd_der = SubjectPublicKeyInfo::from_der(&der).expect("from der");
801+
assert_eq!(kp.der_bytes(), pkd_der.der_bytes());
802+
}
803+
}
804+
719805
#[test]
720806
fn test_algorithm() {
721807
let rng = SystemRandom::new();

rcgen/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub use error::{Error, InvalidAsn1String};
6161
pub use key_pair::PublicKeyData;
6262
#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))]
6363
pub use key_pair::RsaKeySize;
64-
pub use key_pair::{KeyPair, RemoteKeyPair};
64+
pub use key_pair::{KeyPair, RemoteKeyPair, SubjectPublicKeyInfo};
6565
#[cfg(feature = "crypto")]
6666
use ring_like::digest;
6767
pub use sign_algo::algo::*;

0 commit comments

Comments
 (0)