Skip to main content

rcgen/
csr.rs

1use std::hash::Hash;
2
3#[cfg(feature = "pem")]
4use pem::Pem;
5use pki_types::CertificateSigningRequestDer;
6
7#[cfg(feature = "pem")]
8use crate::ENCODE_CONFIG;
9use crate::{
10	Certificate, CertificateParams, Error, Issuer, PublicKeyData, SignatureAlgorithm, SigningKey,
11};
12#[cfg(feature = "x509-parser")]
13use crate::{DistinguishedName, ExtendedKeyUsagePurpose, IsCa, KeyUsagePurpose, SanType};
14
15/// A public key, extracted from a CSR
16#[derive(Clone, Debug, PartialEq, Eq, Hash)]
17pub struct PublicKey {
18	raw: Vec<u8>,
19	alg: &'static SignatureAlgorithm,
20}
21
22impl PublicKey {
23	/// The algorithm used to generate the public key and sign the CSR.
24	pub fn algorithm(&self) -> &SignatureAlgorithm {
25		self.alg
26	}
27}
28
29impl PublicKeyData for PublicKey {
30	fn der_bytes(&self) -> &[u8] {
31		&self.raw
32	}
33
34	fn algorithm(&self) -> &'static SignatureAlgorithm {
35		self.alg
36	}
37}
38
39/// A certificate signing request (CSR) that can be encoded to PEM or DER.
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct CertificateSigningRequest {
42	pub(crate) der: CertificateSigningRequestDer<'static>,
43}
44
45impl CertificateSigningRequest {
46	/// Get the PEM-encoded bytes of the certificate signing request.
47	#[cfg(feature = "pem")]
48	pub fn pem(&self) -> Result<String, Error> {
49		let p = Pem::new("CERTIFICATE REQUEST", &*self.der);
50		Ok(pem::encode_config(&p, ENCODE_CONFIG))
51	}
52
53	/// Get the DER-encoded bytes of the certificate signing request.
54	///
55	/// [`CertificateSigningRequestDer`] implements `Deref<Target = [u8]>` and `AsRef<[u8]>`,
56	/// so you can easily extract the DER bytes from the return value.
57	pub fn der(&self) -> &CertificateSigningRequestDer<'static> {
58		&self.der
59	}
60}
61
62impl From<CertificateSigningRequest> for CertificateSigningRequestDer<'static> {
63	fn from(csr: CertificateSigningRequest) -> Self {
64		csr.der
65	}
66}
67
68/// Parameters for a certificate signing request
69#[derive(Clone, Debug, PartialEq, Eq)]
70pub struct CertificateSigningRequestParams {
71	/// Parameters for the certificate to be signed.
72	pub params: CertificateParams,
73	/// Public key to include in the certificate signing request.
74	pub public_key: PublicKey,
75}
76
77impl CertificateSigningRequestParams {
78	/// Parse and verify a certificate signing request from the ASCII PEM format
79	///
80	/// See [`from_der`](Self::from_der) for more details.
81	#[cfg(all(feature = "pem", feature = "x509-parser"))]
82	pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
83		let csr = pem::parse(pem_str).map_err(|_| Error::CouldNotParseCertificationRequest)?;
84		Self::from_der(&csr.contents().into())
85	}
86
87	/// Parse and verify a certificate signing request from DER-encoded bytes
88	///
89	/// Currently, this supports the following extensions:
90	/// - `Subject Alternative Name` (see [`SanType`])
91	/// - `Key Usage` (see [`KeyUsagePurpose`])
92	/// - `Extended Key Usage` (see [`ExtendedKeyUsagePurpose`])
93	/// - `Basic Constraints` (see [`crate::BasicConstraints`])
94	///
95	/// On encountering other extensions, this function will return [`Error::UnsupportedExtension`].
96	/// If the request's signature is invalid, it will return
97	/// [`Error::InvalidCertificationRequestSignature`].
98	///
99	/// The [`PemObject`] trait is often used to obtain a [`CertificateSigningRequestDer`] from
100	/// PEM input. If you already have a byte slice containing DER, it can trivially be converted
101	/// into [`CertificateSigningRequestDer`] using the [`Into`] trait.
102	///
103	/// [`PemObject`]: pki_types::pem::PemObject
104	#[cfg(feature = "x509-parser")]
105	pub fn from_der(csr: &CertificateSigningRequestDer<'_>) -> Result<Self, Error> {
106		use x509_parser::prelude::FromDer;
107
108		let csr = x509_parser::certification_request::X509CertificationRequest::from_der(csr)
109			.map_err(|_| Error::CouldNotParseCertificationRequest)?
110			.1;
111		csr.verify_signature()
112			.map_err(|_| Error::InvalidCertificationRequestSignature)?;
113		let alg_oid = csr
114			.signature_algorithm
115			.algorithm
116			.iter()
117			.ok_or(Error::CouldNotParseCertificationRequest)?
118			.collect::<Vec<_>>();
119		let alg = SignatureAlgorithm::from_oid(&alg_oid)?;
120
121		let info = &csr.certification_request_info;
122		let mut params = CertificateParams {
123			distinguished_name: DistinguishedName::from_name(&info.subject)?,
124			..CertificateParams::default()
125		};
126		let raw = info.subject_pki.subject_public_key.data.to_vec();
127
128		if let Some(extensions) = csr.requested_extensions() {
129			for ext in extensions {
130				match ext {
131					x509_parser::extensions::ParsedExtension::KeyUsage(key_usage) => {
132						// This x509 parser stores flags in reversed bit BIT STRING order
133						params.key_usages =
134							KeyUsagePurpose::from_u16(key_usage.flags.reverse_bits());
135					},
136					x509_parser::extensions::ParsedExtension::SubjectAlternativeName(san) => {
137						for name in &san.general_names {
138							params
139								.subject_alt_names
140								.push(SanType::try_from_general(name)?);
141						}
142					},
143					x509_parser::extensions::ParsedExtension::ExtendedKeyUsage(eku) => {
144						if eku.any {
145							params.insert_extended_key_usage(ExtendedKeyUsagePurpose::Any);
146						}
147						if eku.server_auth {
148							params.insert_extended_key_usage(ExtendedKeyUsagePurpose::ServerAuth);
149						}
150						if eku.client_auth {
151							params.insert_extended_key_usage(ExtendedKeyUsagePurpose::ClientAuth);
152						}
153						if eku.code_signing {
154							params.insert_extended_key_usage(ExtendedKeyUsagePurpose::CodeSigning);
155						}
156						if eku.email_protection {
157							params.insert_extended_key_usage(
158								ExtendedKeyUsagePurpose::EmailProtection,
159							);
160						}
161						if eku.time_stamping {
162							params.insert_extended_key_usage(ExtendedKeyUsagePurpose::TimeStamping);
163						}
164						if eku.ocsp_signing {
165							params.insert_extended_key_usage(ExtendedKeyUsagePurpose::OcspSigning);
166						}
167						if !eku.other.is_empty() {
168							return Err(Error::UnsupportedExtension);
169						}
170					},
171					x509_parser::extensions::ParsedExtension::BasicConstraints(bc) => {
172						params.is_ca = IsCa::from_basic_constraints(bc)?;
173					},
174					_ => return Err(Error::UnsupportedExtension),
175				}
176			}
177		}
178
179		// Not yet handled:
180		// * name_constraints
181		// and any other extensions.
182
183		Ok(Self {
184			params,
185			public_key: PublicKey { alg, raw },
186		})
187	}
188
189	/// Generate a new certificate based on the requested parameters, signed by the provided
190	/// issuer.
191	///
192	/// The returned certificate will have its issuer field set to the subject of the provided
193	/// `issuer`, and the authority key identifier extension will be populated using the subject
194	/// public key of `issuer`. It will be signed by `issuer_key`.
195	///
196	/// Note that no validation of the `issuer` certificate is performed. Rcgen will not require
197	/// the certificate to be a CA certificate, or have key usage extensions that allow signing.
198	///
199	/// The returned [`Certificate`] may be serialized using [`Certificate::der`] and
200	/// [`Certificate::pem`].
201	pub fn signed_by(&self, issuer: &Issuer<impl SigningKey>) -> Result<Certificate, Error> {
202		Ok(Certificate {
203			der: self
204				.params
205				.serialize_der_with_signer(&self.public_key, issuer)?,
206		})
207	}
208}
209
210#[cfg(all(test, feature = "x509-parser"))]
211mod tests {
212	use x509_parser::certification_request::X509CertificationRequest;
213	use x509_parser::prelude::{FromDer, ParsedExtension};
214
215	use crate::{
216		BasicConstraints, CertificateParams, CertificateSigningRequestParams,
217		ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose,
218	};
219
220	#[test]
221	fn dont_write_sans_extension_if_no_sans_are_present() {
222		let mut params = CertificateParams::default();
223		params.key_usages.push(KeyUsagePurpose::DigitalSignature);
224		let key_pair = KeyPair::generate().unwrap();
225		let csr = params.serialize_request(&key_pair).unwrap();
226		let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap();
227		assert!(!parsed_csr
228			.requested_extensions()
229			.unwrap()
230			.any(|ext| matches!(ext, ParsedExtension::SubjectAlternativeName(_))));
231	}
232
233	#[test]
234	fn write_extension_request_if_ekus_are_present() {
235		let mut params = CertificateParams::default();
236		params
237			.extended_key_usages
238			.push(ExtendedKeyUsagePurpose::ClientAuth);
239		let key_pair = KeyPair::generate().unwrap();
240		let csr = params.serialize_request(&key_pair).unwrap();
241		let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap();
242		let requested_extensions = parsed_csr
243			.requested_extensions()
244			.unwrap()
245			.collect::<Vec<_>>();
246		assert!(matches!(
247			requested_extensions.first().unwrap(),
248			ParsedExtension::ExtendedKeyUsage(_)
249		));
250	}
251
252	#[test]
253	fn write_basic_constraints_if_present() {
254		use x509_parser::extensions::BasicConstraints as B;
255
256		let params = CertificateParams {
257			is_ca: IsCa::ExplicitNoCa,
258			..Default::default()
259		};
260		let key_pair = KeyPair::generate().unwrap();
261		let csr = params.serialize_request(&key_pair).unwrap();
262		let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap();
263		let requested_extensions = parsed_csr
264			.requested_extensions()
265			.unwrap()
266			.collect::<Vec<_>>();
267
268		assert!(matches!(
269			requested_extensions.first().unwrap(),
270			ParsedExtension::BasicConstraints(B {
271				ca: false,
272				path_len_constraint: None
273			})
274		));
275	}
276
277	#[test]
278	fn serialize_and_deserialize_eq_basic_constraints() {
279		let params = CertificateParams {
280			is_ca: IsCa::Ca(BasicConstraints::Constrained(10)),
281			..Default::default()
282		};
283		let key_pair = KeyPair::generate().unwrap();
284		let csr = params.serialize_request(&key_pair).unwrap();
285		let csr_de = CertificateSigningRequestParams::from_der(csr.der()).unwrap();
286
287		assert_eq!(csr_de.params.is_ca, params.is_ca);
288	}
289}