Skip to content
Merged
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
337 changes: 139 additions & 198 deletions rcgen/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ impl CertificateParams {
/// See [`from_ca_cert_der`](Self::from_ca_cert_der) for more details.
#[cfg(all(feature = "pem", feature = "x509-parser"))]
pub fn from_ca_cert_pem(pem_str: &str) -> Result<Self, Error> {
let certificate = pem::parse(pem_str).or(Err(Error::CouldNotParseCertificate))?;
let certificate = pem::parse(pem_str).map_err(|_| Error::CouldNotParseCertificate)?;
Self::from_ca_cert_der(&certificate.contents().into())
}

Expand Down Expand Up @@ -200,208 +200,22 @@ impl CertificateParams {
#[cfg(feature = "x509-parser")]
pub fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result<Self, Error> {
let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert)
.or(Err(Error::CouldNotParseCertificate))?;

let dn = DistinguishedName::from_name(&x509.tbs_certificate.subject)?;
let is_ca = Self::convert_x509_is_ca(&x509)?;
let validity = x509.validity();
let subject_alt_names = Self::convert_x509_subject_alternative_name(&x509)?;
let key_usages = Self::convert_x509_key_usages(&x509)?;
let extended_key_usages = Self::convert_x509_extended_key_usages(&x509)?;
let name_constraints = Self::convert_x509_name_constraints(&x509)?;
let serial_number = Some(x509.serial.to_bytes_be().into());

let key_identifier_method =
x509.iter_extensions()
.find_map(|ext| match ext.parsed_extension() {
x509_parser::extensions::ParsedExtension::SubjectKeyIdentifier(key_id) => {
Some(KeyIdMethod::PreSpecified(key_id.0.into()))
},
_ => None,
});

let key_identifier_method = match key_identifier_method {
Some(method) => method,
None => {
#[cfg(not(feature = "crypto"))]
return Err(Error::UnsupportedSignatureAlgorithm);
#[cfg(feature = "crypto")]
KeyIdMethod::Sha256
},
};
.map_err(|_| Error::CouldNotParseCertificate)?;

Ok(CertificateParams {
is_ca,
subject_alt_names,
key_usages,
extended_key_usages,
name_constraints,
serial_number,
key_identifier_method,
distinguished_name: dn,
not_before: validity.not_before.to_datetime(),
not_after: validity.not_after.to_datetime(),
is_ca: IsCa::from_x509(&x509)?,
subject_alt_names: SanType::from_x509(&x509)?,
key_usages: KeyUsagePurpose::from_x509(&x509)?,
extended_key_usages: ExtendedKeyUsagePurpose::from_x509(&x509)?,
name_constraints: NameConstraints::from_x509(&x509)?,
serial_number: Some(x509.serial.to_bytes_be().into()),
key_identifier_method: KeyIdMethod::from_x509(&x509)?,
distinguished_name: DistinguishedName::from_name(&x509.tbs_certificate.subject)?,
not_before: x509.validity().not_before.to_datetime(),
not_after: x509.validity().not_after.to_datetime(),
..Default::default()
})
}
#[cfg(feature = "x509-parser")]
fn convert_x509_is_ca(
x509: &x509_parser::certificate::X509Certificate<'_>,
) -> Result<IsCa, Error> {
use x509_parser::extensions::BasicConstraints as B;

let basic_constraints = x509
.basic_constraints()
.or(Err(Error::CouldNotParseCertificate))?
.map(|ext| ext.value);

let is_ca = match basic_constraints {
Some(B {
ca: true,
path_len_constraint: Some(n),
}) if *n <= u8::MAX as u32 => IsCa::Ca(BasicConstraints::Constrained(*n as u8)),
Some(B {
ca: true,
path_len_constraint: Some(_),
}) => return Err(Error::CouldNotParseCertificate),
Some(B {
ca: true,
path_len_constraint: None,
}) => IsCa::Ca(BasicConstraints::Unconstrained),
Some(B { ca: false, .. }) => IsCa::ExplicitNoCa,
None => IsCa::NoCa,
};

Ok(is_ca)
}
#[cfg(feature = "x509-parser")]
fn convert_x509_subject_alternative_name(
x509: &x509_parser::certificate::X509Certificate<'_>,
) -> Result<Vec<SanType>, Error> {
let sans = x509
.subject_alternative_name()
.or(Err(Error::CouldNotParseCertificate))?
.map(|ext| &ext.value.general_names);

if let Some(sans) = sans {
let mut subject_alt_names = Vec::with_capacity(sans.len());
for san in sans {
subject_alt_names.push(SanType::try_from_general(san)?);
}
Ok(subject_alt_names)
} else {
Ok(Vec::new())
}
}
#[cfg(feature = "x509-parser")]
fn convert_x509_key_usages(
x509: &x509_parser::certificate::X509Certificate<'_>,
) -> Result<Vec<KeyUsagePurpose>, Error> {
let key_usage = x509
.key_usage()
.or(Err(Error::CouldNotParseCertificate))?
.map(|ext| ext.value);
// This x509 parser stores flags in reversed bit BIT STRING order
let flags = key_usage.map_or(0u16, |k| k.flags).reverse_bits();
Ok(KeyUsagePurpose::from_u16(flags))
}
#[cfg(feature = "x509-parser")]
fn convert_x509_extended_key_usages(
x509: &x509_parser::certificate::X509Certificate<'_>,
) -> Result<Vec<ExtendedKeyUsagePurpose>, Error> {
let extended_key_usage = x509
.extended_key_usage()
.or(Err(Error::CouldNotParseCertificate))?
.map(|ext| ext.value);

let mut extended_key_usages = Vec::new();
if let Some(extended_key_usage) = extended_key_usage {
if extended_key_usage.any {
extended_key_usages.push(ExtendedKeyUsagePurpose::Any);
}
if extended_key_usage.server_auth {
extended_key_usages.push(ExtendedKeyUsagePurpose::ServerAuth);
}
if extended_key_usage.client_auth {
extended_key_usages.push(ExtendedKeyUsagePurpose::ClientAuth);
}
if extended_key_usage.code_signing {
extended_key_usages.push(ExtendedKeyUsagePurpose::CodeSigning);
}
if extended_key_usage.email_protection {
extended_key_usages.push(ExtendedKeyUsagePurpose::EmailProtection);
}
if extended_key_usage.time_stamping {
extended_key_usages.push(ExtendedKeyUsagePurpose::TimeStamping);
}
if extended_key_usage.ocsp_signing {
extended_key_usages.push(ExtendedKeyUsagePurpose::OcspSigning);
}
}
Ok(extended_key_usages)
}
#[cfg(feature = "x509-parser")]
fn convert_x509_name_constraints(
x509: &x509_parser::certificate::X509Certificate<'_>,
) -> Result<Option<NameConstraints>, Error> {
let constraints = x509
.name_constraints()
.or(Err(Error::CouldNotParseCertificate))?
.map(|ext| ext.value);

if let Some(constraints) = constraints {
let permitted_subtrees = if let Some(permitted) = &constraints.permitted_subtrees {
Self::convert_x509_general_subtrees(permitted)?
} else {
Vec::new()
};

let excluded_subtrees = if let Some(excluded) = &constraints.excluded_subtrees {
Self::convert_x509_general_subtrees(excluded)?
} else {
Vec::new()
};

let name_constraints = NameConstraints {
permitted_subtrees,
excluded_subtrees,
};

Ok(Some(name_constraints))
} else {
Ok(None)
}
}
#[cfg(feature = "x509-parser")]
fn convert_x509_general_subtrees(
subtrees: &[x509_parser::extensions::GeneralSubtree<'_>],
) -> Result<Vec<GeneralSubtree>, Error> {
use x509_parser::extensions::GeneralName;

let mut result = Vec::new();
for subtree in subtrees {
let subtree = match &subtree.base {
GeneralName::RFC822Name(s) => GeneralSubtree::Rfc822Name(s.to_string()),
GeneralName::DNSName(s) => GeneralSubtree::DnsName(s.to_string()),
GeneralName::DirectoryName(n) => {
GeneralSubtree::DirectoryName(DistinguishedName::from_name(n)?)
},
GeneralName::IPAddress(bytes) if bytes.len() == 8 => {
let addr: [u8; 4] = bytes[..4].try_into().unwrap();
let mask: [u8; 4] = bytes[4..].try_into().unwrap();
GeneralSubtree::IpAddress(CidrSubnet::V4(addr, mask))
},
GeneralName::IPAddress(bytes) if bytes.len() == 32 => {
let addr: [u8; 16] = bytes[..16].try_into().unwrap();
let mask: [u8; 16] = bytes[16..].try_into().unwrap();
GeneralSubtree::IpAddress(CidrSubnet::V6(addr, mask))
},
_ => continue,
};
result.push(subtree);
}
Ok(result)
}

/// Write a CSR extension request attribute as defined in [RFC 2985].
///
Expand Down Expand Up @@ -987,6 +801,41 @@ pub enum ExtendedKeyUsagePurpose {
}

impl ExtendedKeyUsagePurpose {
#[cfg(feature = "x509-parser")]
fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result<Vec<Self>, Error> {
let extended_key_usage = x509
.extended_key_usage()
.map_err(|_| Error::CouldNotParseCertificate)?
.map(|ext| ext.value);

let mut extended_key_usages = Vec::new();
if let Some(extended_key_usage) = extended_key_usage {
if extended_key_usage.any {
extended_key_usages.push(Self::Any);
}
if extended_key_usage.server_auth {
extended_key_usages.push(Self::ServerAuth);
}
if extended_key_usage.client_auth {
extended_key_usages.push(Self::ClientAuth);
}
if extended_key_usage.code_signing {
extended_key_usages.push(Self::CodeSigning);
}
if extended_key_usage.email_protection {
extended_key_usages.push(Self::EmailProtection);
}
if extended_key_usage.time_stamping {
extended_key_usages.push(Self::TimeStamping);
}
if extended_key_usage.ocsp_signing {
extended_key_usages.push(Self::OcspSigning);
}
}

Ok(extended_key_usages)
}

fn oid(&self) -> &[u64] {
use ExtendedKeyUsagePurpose::*;
match self {
Expand Down Expand Up @@ -1017,6 +866,37 @@ pub struct NameConstraints {
}

impl NameConstraints {
#[cfg(feature = "x509-parser")]
fn from_x509(
x509: &x509_parser::certificate::X509Certificate<'_>,
) -> Result<Option<Self>, Error> {
let constraints = x509
.name_constraints()
.map_err(|_| Error::CouldNotParseCertificate)?
.map(|ext| ext.value);

let Some(constraints) = constraints else {
return Ok(None);
};

let permitted_subtrees = if let Some(permitted) = &constraints.permitted_subtrees {
GeneralSubtree::from_x509(permitted)?
} else {
Vec::new()
};

let excluded_subtrees = if let Some(excluded) = &constraints.excluded_subtrees {
GeneralSubtree::from_x509(excluded)?
} else {
Vec::new()
};

Ok(Some(Self {
permitted_subtrees,
excluded_subtrees,
}))
}

fn is_empty(&self) -> bool {
self.permitted_subtrees.is_empty() && self.excluded_subtrees.is_empty()
}
Expand All @@ -1039,6 +919,38 @@ pub enum GeneralSubtree {
}

impl GeneralSubtree {
#[cfg(feature = "x509-parser")]
fn from_x509(
subtrees: &[x509_parser::extensions::GeneralSubtree<'_>],
) -> Result<Vec<Self>, Error> {
use x509_parser::extensions::GeneralName;

let mut result = Vec::new();
for subtree in subtrees {
let subtree = match &subtree.base {
GeneralName::RFC822Name(s) => Self::Rfc822Name(s.to_string()),
GeneralName::DNSName(s) => Self::DnsName(s.to_string()),
GeneralName::DirectoryName(n) => {
Self::DirectoryName(DistinguishedName::from_name(n)?)
},
GeneralName::IPAddress(bytes) if bytes.len() == 8 => {
let addr: [u8; 4] = bytes[..4].try_into().unwrap();
let mask: [u8; 4] = bytes[4..].try_into().unwrap();
Self::IpAddress(CidrSubnet::V4(addr, mask))
},
GeneralName::IPAddress(bytes) if bytes.len() == 32 => {
let addr: [u8; 16] = bytes[..16].try_into().unwrap();
let mask: [u8; 16] = bytes[16..].try_into().unwrap();
Self::IpAddress(CidrSubnet::V6(addr, mask))
},
_ => continue,
};
result.push(subtree);
}

Ok(result)
}

fn tag(&self) -> u64 {
// Defined in the GeneralName list in
// https://tools.ietf.org/html/rfc5280#page-38
Expand Down Expand Up @@ -1178,6 +1090,35 @@ pub enum IsCa {
Ca(BasicConstraints),
}

impl IsCa {
#[cfg(feature = "x509-parser")]
fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result<Self, Error> {
use x509_parser::extensions::BasicConstraints as B;

let basic_constraints = x509
.basic_constraints()
.map_err(|_| Error::CouldNotParseCertificate)?
.map(|ext| ext.value);

Ok(match basic_constraints {
Some(B {
ca: true,
path_len_constraint: Some(n),
}) if *n <= u8::MAX as u32 => Self::Ca(BasicConstraints::Constrained(*n as u8)),
Some(B {
ca: true,
path_len_constraint: Some(_),
}) => return Err(Error::CouldNotParseCertificate),
Some(B {
ca: true,
path_len_constraint: None,
}) => Self::Ca(BasicConstraints::Unconstrained),
Some(B { ca: false, .. }) => Self::ExplicitNoCa,
None => Self::NoCa,
})
}
}

/// The path length constraint (only relevant for CA certificates)
///
/// Sets an optional upper limit on the length of the intermediate certificate chain
Expand Down
2 changes: 1 addition & 1 deletion rcgen/src/csr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl CertificateSigningRequestParams {
/// See [`from_der`](Self::from_der) for more details.
#[cfg(all(feature = "pem", feature = "x509-parser"))]
pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
let csr = pem::parse(pem_str).or(Err(Error::CouldNotParseCertificationRequest))?;
let csr = pem::parse(pem_str).map_err(|_| Error::CouldNotParseCertificationRequest)?;
Self::from_der(&csr.contents().into())
}

Expand Down
Loading
Loading