Skip to content

Commit 72ca3a4

Browse files
committed
Privatize CertificateParams::from_ca_cert_der()
1 parent 6563b50 commit 72ca3a4

File tree

4 files changed

+129
-134
lines changed

4 files changed

+129
-134
lines changed

rcgen/src/certificate.rs

Lines changed: 89 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -165,38 +165,8 @@ impl CertificateParams {
165165
.derive(key.subject_public_key_info())
166166
}
167167

168-
/// Parses an existing ca certificate from the ASCII PEM format.
169-
///
170-
/// See [`from_ca_cert_der`](Self::from_ca_cert_der) for more details.
171-
#[cfg(all(feature = "pem", feature = "x509-parser"))]
172-
pub fn from_ca_cert_pem(pem_str: &str) -> Result<Self, Error> {
173-
let certificate = pem::parse(pem_str).map_err(|_| Error::CouldNotParseCertificate)?;
174-
Self::from_ca_cert_der(&certificate.contents().into())
175-
}
176-
177-
/// Parses an existing ca certificate from the DER format.
178-
///
179-
/// This function is only of use if you have an existing CA certificate
180-
/// you would like to use to sign a certificate generated by `rcgen`.
181-
/// By providing the constructed [`CertificateParams`] and the [`SigningKey`]
182-
/// associated with your existing `ca_cert` you can use [`CertificateParams::signed_by()`]
183-
/// or [`crate::CertificateSigningRequestParams::signed_by()`] to issue new certificates
184-
/// using the CA cert.
185-
///
186-
/// In general this function only extracts the information needed for signing.
187-
/// Other attributes of the [`Certificate`] may be left as defaults.
188-
///
189-
/// This function assumes the provided certificate is a CA. It will not check
190-
/// for the presence of the `BasicConstraints` extension, or perform any other
191-
/// validation.
192-
///
193-
/// [`rustls_pemfile::certs()`] is often used to obtain a [`CertificateDer`] from PEM input.
194-
/// If you already have a byte slice containing DER, it can trivially be converted into
195-
/// [`CertificateDer`] using the [`Into`] trait.
196-
///
197-
/// [`rustls_pemfile::certs()`]: https://docs.rs/rustls-pemfile/latest/rustls_pemfile/fn.certs.html
198-
#[cfg(feature = "x509-parser")]
199-
pub fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result<Self, Error> {
168+
#[cfg(all(test, feature = "x509-parser"))]
169+
pub(crate) fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result<Self, Error> {
200170
let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert)
201171
.map_err(|_| Error::CouldNotParseCertificate)?;
202172

@@ -801,7 +771,7 @@ pub enum ExtendedKeyUsagePurpose {
801771
}
802772

803773
impl ExtendedKeyUsagePurpose {
804-
#[cfg(feature = "x509-parser")]
774+
#[cfg(all(test, feature = "x509-parser"))]
805775
fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result<Vec<Self>, Error> {
806776
let extended_key_usage = x509
807777
.extended_key_usage()
@@ -866,7 +836,7 @@ pub struct NameConstraints {
866836
}
867837

868838
impl NameConstraints {
869-
#[cfg(feature = "x509-parser")]
839+
#[cfg(all(test, feature = "x509-parser"))]
870840
fn from_x509(
871841
x509: &x509_parser::certificate::X509Certificate<'_>,
872842
) -> Result<Option<Self>, Error> {
@@ -919,7 +889,7 @@ pub enum GeneralSubtree {
919889
}
920890

921891
impl GeneralSubtree {
922-
#[cfg(feature = "x509-parser")]
892+
#[cfg(all(test, feature = "x509-parser"))]
923893
fn from_x509(
924894
subtrees: &[x509_parser::extensions::GeneralSubtree<'_>],
925895
) -> Result<Vec<Self>, Error> {
@@ -1091,7 +1061,7 @@ pub enum IsCa {
10911061
}
10921062

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

@@ -1133,11 +1103,16 @@ pub enum BasicConstraints {
11331103

11341104
#[cfg(test)]
11351105
mod tests {
1106+
#[cfg(feature = "x509-parser")]
1107+
use std::net::Ipv4Addr;
1108+
11361109
#[cfg(feature = "x509-parser")]
11371110
use pki_types::pem::PemObject;
11381111

11391112
#[cfg(feature = "pem")]
11401113
use super::*;
1114+
#[cfg(feature = "x509-parser")]
1115+
use crate::DnValue;
11411116
#[cfg(feature = "crypto")]
11421117
use crate::KeyPair;
11431118

@@ -1293,7 +1268,84 @@ mod tests {
12931268
}
12941269
}
12951270

1296-
#[cfg(all(feature = "x509-parser"))]
1271+
#[cfg(feature = "x509-parser")]
1272+
#[test]
1273+
fn parse_other_name_alt_name() {
1274+
// Create and serialize a certificate with an alternative name containing an "OtherName".
1275+
let mut params = CertificateParams::default();
1276+
let other_name = SanType::OtherName((vec![1, 2, 3, 4], "Foo".into()));
1277+
params.subject_alt_names.push(other_name.clone());
1278+
let key_pair = KeyPair::generate().unwrap();
1279+
let cert = params.self_signed(&key_pair).unwrap();
1280+
1281+
// We should be able to parse the certificate with x509-parser.
1282+
assert!(x509_parser::parse_x509_certificate(cert.der()).is_ok());
1283+
1284+
// We should be able to reconstitute params from the DER using x509-parser.
1285+
let params_from_cert = CertificateParams::from_ca_cert_der(cert.der()).unwrap();
1286+
1287+
// We should find the expected distinguished name in the reconstituted params.
1288+
let expected_alt_names = &[&other_name];
1289+
let subject_alt_names = params_from_cert
1290+
.subject_alt_names
1291+
.iter()
1292+
.collect::<Vec<_>>();
1293+
assert_eq!(subject_alt_names, expected_alt_names);
1294+
}
1295+
1296+
#[cfg(feature = "x509-parser")]
1297+
#[test]
1298+
fn parse_ia5string_subject() {
1299+
// Create and serialize a certificate with a subject containing an IA5String email address.
1300+
let email_address_dn_type = DnType::CustomDnType(vec![1, 2, 840, 113549, 1, 9, 1]); // id-emailAddress
1301+
let email_address_dn_value = DnValue::Ia5String("[email protected]".try_into().unwrap());
1302+
let mut params = CertificateParams::new(vec!["crabs".to_owned()]).unwrap();
1303+
params.distinguished_name = DistinguishedName::new();
1304+
params.distinguished_name.push(
1305+
email_address_dn_type.clone(),
1306+
email_address_dn_value.clone(),
1307+
);
1308+
let key_pair = KeyPair::generate().unwrap();
1309+
let cert = params.self_signed(&key_pair).unwrap();
1310+
1311+
// We should be able to parse the certificate with x509-parser.
1312+
assert!(x509_parser::parse_x509_certificate(cert.der()).is_ok());
1313+
1314+
// We should be able to reconstitute params from the DER using x509-parser.
1315+
let params_from_cert = CertificateParams::from_ca_cert_der(cert.der()).unwrap();
1316+
1317+
// We should find the expected distinguished name in the reconstituted params.
1318+
let expected_names = &[(&email_address_dn_type, &email_address_dn_value)];
1319+
let names = params_from_cert
1320+
.distinguished_name
1321+
.iter()
1322+
.collect::<Vec<(_, _)>>();
1323+
assert_eq!(names, expected_names);
1324+
}
1325+
1326+
#[cfg(feature = "x509-parser")]
1327+
#[test]
1328+
fn converts_from_ip() {
1329+
let ip = Ipv4Addr::new(2, 4, 6, 8);
1330+
let ip_san = SanType::IpAddress(IpAddr::V4(ip));
1331+
1332+
let mut params = CertificateParams::new(vec!["crabs".to_owned()]).unwrap();
1333+
let ca_key = KeyPair::generate().unwrap();
1334+
1335+
// Add the SAN we want to test the parsing for
1336+
params.subject_alt_names.push(ip_san.clone());
1337+
1338+
// Because we're using a function for CA certificates
1339+
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
1340+
1341+
// Serialize our cert that has our chosen san, so we can testing parsing/deserializing it.
1342+
let cert = params.self_signed(&ca_key).unwrap();
1343+
1344+
let actual = CertificateParams::from_ca_cert_der(cert.der()).unwrap();
1345+
assert!(actual.subject_alt_names.contains(&ip_san));
1346+
}
1347+
1348+
#[cfg(feature = "x509-parser")]
12971349
mod test_key_identifier_from_ca {
12981350
use super::*;
12991351

rcgen/src/csr.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,42 @@ impl CertificateSigningRequestParams {
208208
})
209209
}
210210
}
211+
212+
#[cfg(all(test, feature = "x509-parser"))]
213+
mod tests {
214+
use crate::{CertificateParams, ExtendedKeyUsagePurpose, KeyPair, KeyUsagePurpose};
215+
use x509_parser::certification_request::X509CertificationRequest;
216+
use x509_parser::prelude::{FromDer, ParsedExtension};
217+
218+
#[test]
219+
fn dont_write_sans_extension_if_no_sans_are_present() {
220+
let mut params = CertificateParams::default();
221+
params.key_usages.push(KeyUsagePurpose::DigitalSignature);
222+
let key_pair = KeyPair::generate().unwrap();
223+
let csr = params.serialize_request(&key_pair).unwrap();
224+
let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap();
225+
assert!(!parsed_csr
226+
.requested_extensions()
227+
.unwrap()
228+
.any(|ext| matches!(ext, ParsedExtension::SubjectAlternativeName(_))));
229+
}
230+
231+
#[test]
232+
fn write_extension_request_if_ekus_are_present() {
233+
let mut params = CertificateParams::default();
234+
params
235+
.extended_key_usages
236+
.push(ExtendedKeyUsagePurpose::ClientAuth);
237+
let key_pair = KeyPair::generate().unwrap();
238+
let csr = params.serialize_request(&key_pair).unwrap();
239+
let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap();
240+
let requested_extensions = parsed_csr
241+
.requested_extensions()
242+
.unwrap()
243+
.collect::<Vec<_>>();
244+
assert!(matches!(
245+
requested_extensions.first().unwrap(),
246+
ParsedExtension::ExtendedKeyUsage(_)
247+
));
248+
}
249+
}

rcgen/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ pub enum SanType {
274274
}
275275

276276
impl SanType {
277-
#[cfg(feature = "x509-parser")]
277+
#[cfg(all(test, feature = "x509-parser"))]
278278
fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result<Vec<Self>, Error> {
279279
let sans = x509
280280
.subject_alternative_name()

rcgen/tests/generic.rs

Lines changed: 0 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -38,34 +38,6 @@ mod test_key_params_mismatch {
3838
}
3939
}
4040

41-
#[cfg(feature = "x509-parser")]
42-
mod test_convert_x509_subject_alternative_name {
43-
use rcgen::{BasicConstraints, CertificateParams, IsCa, SanType};
44-
use std::net::{IpAddr, Ipv4Addr};
45-
46-
#[test]
47-
fn converts_from_ip() {
48-
let ip = Ipv4Addr::new(2, 4, 6, 8);
49-
let ip_san = SanType::IpAddress(IpAddr::V4(ip));
50-
51-
let (mut params, ca_key) = super::util::default_params();
52-
53-
// Add the SAN we want to test the parsing for
54-
params.subject_alt_names.push(ip_san.clone());
55-
56-
// Because we're using a function for CA certificates
57-
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
58-
59-
let cert = params.self_signed(&ca_key).unwrap();
60-
61-
// Serialize our cert that has our chosen san, so we can testing parsing/deserializing it.
62-
let ca_der = cert.der();
63-
64-
let actual = CertificateParams::from_ca_cert_der(ca_der).unwrap();
65-
assert!(actual.subject_alt_names.contains(&ip_san));
66-
}
67-
}
68-
6941
#[cfg(feature = "x509-parser")]
7042
mod test_x509_custom_ext {
7143
use crate::util;
@@ -347,74 +319,6 @@ mod test_parse_crl_dps {
347319
}
348320
}
349321

350-
#[cfg(feature = "x509-parser")]
351-
mod test_parse_ia5string_subject {
352-
use crate::util;
353-
use rcgen::DnType::CustomDnType;
354-
use rcgen::{CertificateParams, DistinguishedName, DnValue};
355-
356-
#[test]
357-
fn parse_ia5string_subject() {
358-
// Create and serialize a certificate with a subject containing an IA5String email address.
359-
let email_address_dn_type = CustomDnType(vec![1, 2, 840, 113549, 1, 9, 1]); // id-emailAddress
360-
let email_address_dn_value = DnValue::Ia5String("[email protected]".try_into().unwrap());
361-
let (mut params, key_pair) = util::default_params();
362-
params.distinguished_name = DistinguishedName::new();
363-
params.distinguished_name.push(
364-
email_address_dn_type.clone(),
365-
email_address_dn_value.clone(),
366-
);
367-
let cert = params.self_signed(&key_pair).unwrap();
368-
let cert_der = cert.der();
369-
370-
// We should be able to parse the certificate with x509-parser.
371-
assert!(x509_parser::parse_x509_certificate(cert_der).is_ok());
372-
373-
// We should be able to reconstitute params from the DER using x509-parser.
374-
let params_from_cert = CertificateParams::from_ca_cert_der(cert_der).unwrap();
375-
376-
// We should find the expected distinguished name in the reconstituted params.
377-
let expected_names = &[(&email_address_dn_type, &email_address_dn_value)];
378-
let names = params_from_cert
379-
.distinguished_name
380-
.iter()
381-
.collect::<Vec<(_, _)>>();
382-
assert_eq!(names, expected_names);
383-
}
384-
}
385-
386-
#[cfg(feature = "x509-parser")]
387-
mod test_parse_other_name_alt_name {
388-
use rcgen::{CertificateParams, KeyPair, SanType};
389-
390-
#[test]
391-
fn parse_other_name_alt_name() {
392-
// Create and serialize a certificate with an alternative name containing an "OtherName".
393-
let mut params = CertificateParams::default();
394-
let other_name = SanType::OtherName((vec![1, 2, 3, 4], "Foo".into()));
395-
params.subject_alt_names.push(other_name.clone());
396-
let key_pair = KeyPair::generate().unwrap();
397-
398-
let cert = params.self_signed(&key_pair).unwrap();
399-
400-
let cert_der = cert.der();
401-
402-
// We should be able to parse the certificate with x509-parser.
403-
assert!(x509_parser::parse_x509_certificate(cert_der).is_ok());
404-
405-
// We should be able to reconstitute params from the DER using x509-parser.
406-
let params_from_cert = CertificateParams::from_ca_cert_der(cert_der).unwrap();
407-
408-
// We should find the expected distinguished name in the reconstituted params.
409-
let expected_alt_names = &[&other_name];
410-
let subject_alt_names = params_from_cert
411-
.subject_alt_names
412-
.iter()
413-
.collect::<Vec<_>>();
414-
assert_eq!(subject_alt_names, expected_alt_names);
415-
}
416-
}
417-
418322
#[cfg(feature = "x509-parser")]
419323
mod test_csr_extension_request {
420324
use rcgen::{CertificateParams, ExtendedKeyUsagePurpose, KeyPair, KeyUsagePurpose};

0 commit comments

Comments
 (0)