Skip to content

Commit 486a5a6

Browse files
authored
x509-cert: move CSR builder to x509_cert::request (#1581)
1 parent 75edabf commit 486a5a6

File tree

3 files changed

+145
-121
lines changed

3 files changed

+145
-121
lines changed

x509-cert/src/builder.rs

Lines changed: 7 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ use spki::{
1212
use crate::{
1313
certificate::{Certificate, TbsCertificate, Version},
1414
ext::{AsExtension, Extensions},
15-
name::Name,
16-
request::{attributes::AsAttribute, CertReq, CertReqInfo, ExtensionReq},
1715
serial_number::SerialNumber,
1816
time::Validity,
1917
AlgorithmIdentifier, SubjectPublicKeyInfo,
@@ -29,7 +27,13 @@ use self::profile::BuilderProfile;
2927
)]
3028
pub use self::profile::BuilderProfile as Profile;
3129

32-
const NULL_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.0.0");
30+
#[deprecated(
31+
since = "0.3.0",
32+
note = "please use `x509_cert::request::RequestBuilder` instead"
33+
)]
34+
pub use crate::request::RequestBuilder;
35+
36+
pub(crate) const NULL_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.0.0");
3337

3438
/// Error type
3539
#[derive(Debug)]
@@ -212,90 +216,6 @@ where
212216
}
213217
}
214218

215-
/// Builder for X509 Certificate Requests
216-
///
217-
/// ```
218-
/// # use p256::{pkcs8::DecodePrivateKey, NistP256, ecdsa::DerSignature};
219-
/// # const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("../tests/examples/p256-priv.der");
220-
/// # fn ecdsa_signer() -> ecdsa::SigningKey<NistP256> {
221-
/// # let secret_key = p256::SecretKey::from_pkcs8_der(PKCS8_PRIVATE_KEY_DER).unwrap();
222-
/// # ecdsa::SigningKey::from(secret_key)
223-
/// # }
224-
/// use x509_cert::{
225-
/// builder::{Builder, RequestBuilder},
226-
/// ext::pkix::{name::GeneralName, SubjectAltName},
227-
/// name::Name,
228-
/// };
229-
/// use std::str::FromStr;
230-
///
231-
/// use std::net::{IpAddr, Ipv4Addr};
232-
/// let subject = Name::from_str("CN=service.domination.world").unwrap();
233-
///
234-
/// let signer = ecdsa_signer();
235-
/// let mut builder = RequestBuilder::new(subject).expect("Create certificate request");
236-
/// builder
237-
/// .add_extension(&SubjectAltName(vec![GeneralName::from(IpAddr::V4(
238-
/// Ipv4Addr::new(192, 0, 2, 0),
239-
/// ))]))
240-
/// .unwrap();
241-
///
242-
/// let cert_req = builder.build::<_, DerSignature>(&signer).unwrap();
243-
/// ```
244-
pub struct RequestBuilder {
245-
info: CertReqInfo,
246-
extension_req: ExtensionReq,
247-
}
248-
249-
impl RequestBuilder {
250-
/// Creates a new certificate request builder
251-
pub fn new(subject: Name) -> Result<Self> {
252-
let version = Default::default();
253-
254-
let algorithm = AlgorithmIdentifier {
255-
oid: NULL_OID,
256-
parameters: None,
257-
};
258-
let public_key = SubjectPublicKeyInfo {
259-
algorithm,
260-
subject_public_key: BitString::from_bytes(&[]).expect("unable to parse empty object"),
261-
};
262-
263-
let attributes = Default::default();
264-
let extension_req = Default::default();
265-
266-
Ok(Self {
267-
info: CertReqInfo {
268-
version,
269-
subject,
270-
public_key,
271-
attributes,
272-
},
273-
extension_req,
274-
})
275-
}
276-
277-
/// Add an extension to this certificate request
278-
///
279-
/// Extensions need to implement [`AsExtension`], examples may be found in
280-
/// in [`AsExtension` documentation](../ext/trait.AsExtension.html#examples) or
281-
/// [the implementors](../ext/trait.AsExtension.html#implementors).
282-
pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> {
283-
let ext = extension.to_extension(&self.info.subject, &self.extension_req.0)?;
284-
285-
self.extension_req.0.push(ext);
286-
287-
Ok(())
288-
}
289-
290-
/// Add an attribute to this certificate request
291-
pub fn add_attribute<A: AsAttribute>(&mut self, attribute: &A) -> Result<()> {
292-
let attr = attribute.to_attribute()?;
293-
294-
self.info.attributes.insert(attr)?;
295-
Ok(())
296-
}
297-
}
298-
299219
/// Trait for X509 builders
300220
///
301221
/// This trait defines the interface between builder and the signers.
@@ -404,40 +324,6 @@ where
404324
}
405325
}
406326

407-
impl Builder for RequestBuilder {
408-
type Output = CertReq;
409-
410-
fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
411-
where
412-
S: Keypair + DynSignatureAlgorithmIdentifier,
413-
S::VerifyingKey: EncodePublicKey,
414-
{
415-
let verifying_key = signer.verifying_key();
416-
let public_key = SubjectPublicKeyInfo::from_key(&verifying_key)?;
417-
self.info.public_key = public_key;
418-
419-
self.info
420-
.attributes
421-
.insert(self.extension_req.clone().try_into()?)?;
422-
423-
self.info.to_der().map_err(Error::from)
424-
}
425-
426-
fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
427-
where
428-
S: Keypair + DynSignatureAlgorithmIdentifier,
429-
S::VerifyingKey: EncodePublicKey,
430-
{
431-
let algorithm = signer.signature_algorithm_identifier()?;
432-
433-
Ok(CertReq {
434-
info: self.info,
435-
algorithm,
436-
signature,
437-
})
438-
}
439-
}
440-
441327
/// Trait for async X509 builders
442328
///
443329
/// This trait defines the interface between builder and the signers.

x509-cert/src/request.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ use der::{
1919
#[cfg(feature = "pem")]
2020
use der::pem::PemLabel;
2121

22+
#[cfg(feature = "builder")]
23+
mod builder;
24+
25+
#[cfg(feature = "builder")]
26+
pub use self::builder::RequestBuilder;
27+
2228
/// Version identifier for certification request information.
2329
///
2430
/// (RFC 2986 designates `0` as the only valid version)

x509-cert/src/request/builder.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use alloc::vec;
2+
3+
use der::{asn1::BitString, Encode};
4+
use signature::Keypair;
5+
use spki::{
6+
AlgorithmIdentifier, DynSignatureAlgorithmIdentifier, EncodePublicKey, SubjectPublicKeyInfo,
7+
};
8+
9+
use crate::{
10+
builder::{Builder, Error, Result, NULL_OID},
11+
ext::AsExtension,
12+
name::Name,
13+
request::{attributes::AsAttribute, CertReq, CertReqInfo, ExtensionReq},
14+
};
15+
16+
/// Builder for X509 Certificate Requests (CSR)
17+
///
18+
/// ```
19+
/// # use p256::{pkcs8::DecodePrivateKey, NistP256, ecdsa::DerSignature};
20+
/// # const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("../../tests/examples/p256-priv.der");
21+
/// # fn ecdsa_signer() -> ecdsa::SigningKey<NistP256> {
22+
/// # let secret_key = p256::SecretKey::from_pkcs8_der(PKCS8_PRIVATE_KEY_DER).unwrap();
23+
/// # ecdsa::SigningKey::from(secret_key)
24+
/// # }
25+
/// use x509_cert::{
26+
/// builder::{Builder, RequestBuilder},
27+
/// ext::pkix::{name::GeneralName, SubjectAltName},
28+
/// name::Name,
29+
/// };
30+
/// use std::str::FromStr;
31+
///
32+
/// use std::net::{IpAddr, Ipv4Addr};
33+
/// let subject = Name::from_str("CN=service.domination.world").unwrap();
34+
///
35+
/// let signer = ecdsa_signer();
36+
/// let mut builder = RequestBuilder::new(subject).expect("Create certificate request");
37+
/// builder
38+
/// .add_extension(&SubjectAltName(vec![GeneralName::from(IpAddr::V4(
39+
/// Ipv4Addr::new(192, 0, 2, 0),
40+
/// ))]))
41+
/// .unwrap();
42+
///
43+
/// let cert_req = builder.build::<_, DerSignature>(&signer).unwrap();
44+
/// ```
45+
pub struct RequestBuilder {
46+
info: CertReqInfo,
47+
extension_req: ExtensionReq,
48+
}
49+
50+
impl RequestBuilder {
51+
/// Creates a new certificate request builder
52+
pub fn new(subject: Name) -> Result<Self> {
53+
let version = Default::default();
54+
55+
let algorithm = AlgorithmIdentifier {
56+
oid: NULL_OID,
57+
parameters: None,
58+
};
59+
let public_key = SubjectPublicKeyInfo {
60+
algorithm,
61+
subject_public_key: BitString::from_bytes(&[]).expect("unable to parse empty object"),
62+
};
63+
64+
let attributes = Default::default();
65+
let extension_req = Default::default();
66+
67+
Ok(Self {
68+
info: CertReqInfo {
69+
version,
70+
subject,
71+
public_key,
72+
attributes,
73+
},
74+
extension_req,
75+
})
76+
}
77+
78+
/// Add an extension to this certificate request
79+
///
80+
/// Extensions need to implement [`AsExtension`], examples may be found in
81+
/// in [`AsExtension` documentation](../ext/trait.AsExtension.html#examples) or
82+
/// [the implementors](../ext/trait.AsExtension.html#implementors).
83+
pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> {
84+
let ext = extension.to_extension(&self.info.subject, &self.extension_req.0)?;
85+
86+
self.extension_req.0.push(ext);
87+
88+
Ok(())
89+
}
90+
91+
/// Add an attribute to this certificate request
92+
pub fn add_attribute<A: AsAttribute>(&mut self, attribute: &A) -> Result<()> {
93+
let attr = attribute.to_attribute()?;
94+
95+
self.info.attributes.insert(attr)?;
96+
Ok(())
97+
}
98+
}
99+
100+
impl Builder for RequestBuilder {
101+
type Output = CertReq;
102+
103+
fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
104+
where
105+
S: Keypair + DynSignatureAlgorithmIdentifier,
106+
S::VerifyingKey: EncodePublicKey,
107+
{
108+
let verifying_key = signer.verifying_key();
109+
let public_key = SubjectPublicKeyInfo::from_key(&verifying_key)?;
110+
self.info.public_key = public_key;
111+
112+
self.info
113+
.attributes
114+
.insert(self.extension_req.clone().try_into()?)?;
115+
116+
self.info.to_der().map_err(Error::from)
117+
}
118+
119+
fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
120+
where
121+
S: Keypair + DynSignatureAlgorithmIdentifier,
122+
S::VerifyingKey: EncodePublicKey,
123+
{
124+
let algorithm = signer.signature_algorithm_identifier()?;
125+
126+
Ok(CertReq {
127+
info: self.info,
128+
algorithm,
129+
signature,
130+
})
131+
}
132+
}

0 commit comments

Comments
 (0)