1 #![cfg(all(feature = "builder", feature = "pem"))]
2
3 use der::{asn1::PrintableString, pem::LineEnding, Decode, Encode, EncodePem};
4 use p256::{ecdsa::DerSignature, pkcs8::DecodePrivateKey, NistP256};
5 use rsa::pkcs1::DecodeRsaPrivateKey;
6 use rsa::pkcs1v15::SigningKey;
7 use sha2::Sha256;
8 use spki::SubjectPublicKeyInfoOwned;
9 use std::{str::FromStr, time::Duration};
10 use x509_cert::{
11 builder::{Builder, CertificateBuilder, Profile, RequestBuilder},
12 ext::pkix::{
13 name::{DirectoryString, GeneralName},
14 SubjectAltName,
15 },
16 name::Name,
17 request,
18 serial_number::SerialNumber,
19 time::Validity,
20 };
21 use x509_cert_test_support::{openssl, zlint};
22
23 const RSA_2048_DER_EXAMPLE: &[u8] = include_bytes!("examples/rsa2048-pub.der");
24 const PKCS8_PUBLIC_KEY_DER: &[u8] = include_bytes!("examples/p256-pub.der");
25
26 #[test]
root_ca_certificate()27 fn root_ca_certificate() {
28 let serial_number = SerialNumber::from(42u32);
29 let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
30 let profile = Profile::Root;
31 let subject = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US")
32 .unwrap()
33 .to_der()
34 .unwrap();
35 let subject = Name::from_der(&subject).unwrap();
36 let pub_key =
37 SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key");
38
39 let signer = rsa_signer();
40 let builder =
41 CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer)
42 .expect("Create certificate");
43
44 let certificate = builder.build().unwrap();
45
46 let pem = certificate.to_pem(LineEnding::LF).expect("generate pem");
47 println!("{}", openssl::check_certificate(pem.as_bytes()));
48
49 let ignored = &[];
50 zlint::check_certificate(pem.as_bytes(), ignored);
51 }
52
53 #[test]
root_ca_certificate_ecdsa()54 fn root_ca_certificate_ecdsa() {
55 let serial_number = SerialNumber::from(42u32);
56 let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
57 let profile = Profile::Root;
58 let subject = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US")
59 .unwrap()
60 .to_der()
61 .unwrap();
62 let subject = Name::from_der(&subject).unwrap();
63 let pub_key =
64 SubjectPublicKeyInfoOwned::try_from(PKCS8_PUBLIC_KEY_DER).expect("get ecdsa pub key");
65
66 let signer = ecdsa_signer();
67 let builder =
68 CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer)
69 .expect("Create certificate");
70
71 let certificate = builder.build::<DerSignature>().unwrap();
72
73 let pem = certificate.to_pem(LineEnding::LF).expect("generate pem");
74 println!("{}", openssl::check_certificate(pem.as_bytes()));
75
76 let ignored = &[];
77 zlint::check_certificate(pem.as_bytes(), ignored);
78 }
79
80 #[test]
sub_ca_certificate()81 fn sub_ca_certificate() {
82 let serial_number = SerialNumber::from(42u32);
83 let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
84
85 let issuer =
86 Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap();
87 let profile = Profile::SubCA {
88 issuer,
89 path_len_constraint: Some(0),
90 };
91
92 let subject =
93 Name::from_str("CN=World domination task force,O=World domination Inc,C=US").unwrap();
94 let pub_key =
95 SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key");
96
97 let signer = ecdsa_signer();
98 let builder =
99 CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer)
100 .expect("Create certificate");
101
102 let certificate = builder.build::<DerSignature>().unwrap();
103
104 let pem = certificate.to_pem(LineEnding::LF).expect("generate pem");
105 println!("{}", openssl::check_certificate(pem.as_bytes()));
106
107 // TODO(baloo): not too sure we should tackle those in this API.
108 let ignored = &[
109 "w_sub_ca_aia_missing",
110 "e_sub_ca_crl_distribution_points_missing",
111 "e_sub_ca_certificate_policies_missing",
112 "w_sub_ca_aia_does_not_contain_issuing_ca_url",
113 ];
114
115 zlint::check_certificate(pem.as_bytes(), ignored);
116 }
117
118 #[test]
leaf_certificate()119 fn leaf_certificate() {
120 let serial_number = SerialNumber::from(42u32);
121 let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
122
123 let issuer =
124 Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap();
125 let profile = Profile::Leaf {
126 issuer: issuer.clone(),
127 enable_key_agreement: false,
128 enable_key_encipherment: false,
129 #[cfg(feature = "hazmat")]
130 include_subject_key_identifier: true,
131 };
132
133 let subject = Name::from_str("CN=service.domination.world").unwrap();
134 let pub_key =
135 SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key");
136
137 let signer = ecdsa_signer();
138 let builder = CertificateBuilder::new(
139 profile,
140 serial_number.clone(),
141 validity,
142 subject.clone(),
143 pub_key.clone(),
144 &signer,
145 )
146 .expect("Create certificate");
147
148 let certificate = builder.build::<DerSignature>().unwrap();
149
150 let pem = certificate.to_pem(LineEnding::LF).expect("generate pem");
151 println!("{}", openssl::check_certificate(pem.as_bytes()));
152
153 // TODO(baloo): not too sure we should tackle those in this API.
154 let ignored = vec![
155 "e_sub_cert_aia_missing",
156 "e_sub_cert_crl_distribution_points_missing",
157 "w_sub_cert_aia_does_not_contain_issuing_ca_url",
158 // Missing policies
159 "e_sub_cert_certificate_policies_missing",
160 "e_sub_cert_cert_policy_empty",
161 // Needs to be added by the end-user
162 "e_sub_cert_aia_does_not_contain_ocsp_url",
163 // SAN needs to include DNS name (if used)
164 "e_ext_san_missing",
165 "e_subject_common_name_not_exactly_from_san",
166 // Extended key usage needs to be added by end-user and is use-case dependent
167 "e_sub_cert_eku_missing",
168 ];
169
170 zlint::check_certificate(pem.as_bytes(), &ignored);
171
172 #[cfg(feature = "hazmat")]
173 {
174 let profile = Profile::Leaf {
175 issuer,
176 enable_key_agreement: false,
177 enable_key_encipherment: false,
178 include_subject_key_identifier: false,
179 };
180 let builder =
181 CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer)
182 .expect("Create certificate");
183
184 let certificate = builder.build::<DerSignature>().unwrap();
185
186 let pem = certificate.to_pem(LineEnding::LF).expect("generate pem");
187 println!("{}", openssl::check_certificate(pem.as_bytes()));
188
189 // Ignore warning about leaf not having SKI extension (this is a warning not a fail, as
190 // denoted by the `w_` prefix.
191 let mut ignored = ignored;
192 ignored.push("w_ext_subject_key_identifier_missing_sub_cert");
193 zlint::check_certificate(pem.as_bytes(), &ignored);
194 }
195 }
196
197 #[test]
pss_certificate()198 fn pss_certificate() {
199 let serial_number = SerialNumber::from(42u32);
200 let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
201
202 let issuer =
203 Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap();
204 let profile = Profile::Leaf {
205 issuer,
206 enable_key_agreement: false,
207 enable_key_encipherment: false,
208 #[cfg(feature = "hazmat")]
209 include_subject_key_identifier: true,
210 };
211
212 let subject = Name::from_str("CN=service.domination.world").unwrap();
213 let pub_key =
214 SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key");
215
216 let signer = rsa_pss_signer();
217 let builder =
218 CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer)
219 .expect("Create certificate");
220
221 let certificate = builder
222 .build_with_rng::<rsa::pss::Signature>(&mut rand::thread_rng())
223 .unwrap();
224
225 let pem = certificate.to_pem(LineEnding::LF).expect("generate pem");
226 println!("{}", openssl::check_certificate(pem.as_bytes()));
227
228 // TODO(baloo): not too sure we should tackle those in this API.
229 let ignored = &[
230 "e_sub_cert_aia_missing",
231 "e_sub_cert_crl_distribution_points_missing",
232 "w_sub_cert_aia_does_not_contain_issuing_ca_url",
233 // Missing policies
234 "e_sub_cert_certificate_policies_missing",
235 "e_sub_cert_cert_policy_empty",
236 // Needs to be added by the end-user
237 "e_sub_cert_aia_does_not_contain_ocsp_url",
238 // SAN needs to include DNS name (if used)
239 "e_ext_san_missing",
240 "e_subject_common_name_not_exactly_from_san",
241 // Extended key usage needs to be added by end-user and is use-case dependent
242 "e_sub_cert_eku_missing",
243 // zlint warns on RSAPSS signature algorithms
244 "e_signature_algorithm_not_supported",
245 ];
246
247 zlint::check_certificate(pem.as_bytes(), ignored);
248 }
249
250 const RSA_2048_PRIV_DER_EXAMPLE: &[u8] = include_bytes!("examples/rsa2048-priv.der");
251
rsa_signer() -> SigningKey<Sha256>252 fn rsa_signer() -> SigningKey<Sha256> {
253 let private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap();
254 SigningKey::<Sha256>::new(private_key)
255 }
256
rsa_pss_signer() -> rsa::pss::SigningKey<Sha256>257 fn rsa_pss_signer() -> rsa::pss::SigningKey<Sha256> {
258 let private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap();
259 rsa::pss::SigningKey::<Sha256>::new(private_key)
260 }
261
262 const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("examples/p256-priv.der");
263
ecdsa_signer() -> ecdsa::SigningKey<NistP256>264 fn ecdsa_signer() -> ecdsa::SigningKey<NistP256> {
265 let secret_key = p256::SecretKey::from_pkcs8_der(PKCS8_PRIVATE_KEY_DER).unwrap();
266 ecdsa::SigningKey::from(secret_key)
267 }
268
269 #[test]
certificate_request()270 fn certificate_request() {
271 use std::net::{IpAddr, Ipv4Addr};
272 let subject = Name::from_str("CN=service.domination.world").unwrap();
273
274 let signer = ecdsa_signer();
275 let mut builder = RequestBuilder::new(subject, &signer).expect("Create certificate request");
276 builder
277 .add_extension(&SubjectAltName(vec![GeneralName::from(IpAddr::V4(
278 Ipv4Addr::new(192, 0, 2, 0),
279 ))]))
280 .unwrap();
281
282 let cert_req = builder.build::<DerSignature>().unwrap();
283 let pem = cert_req.to_pem(LineEnding::LF).expect("generate pem");
284 use std::fs::File;
285 use std::io::Write;
286 let mut file = File::create("/tmp/ecdsa.csr").expect("create pem file");
287 file.write_all(pem.as_bytes()).expect("Create pem file");
288 println!("{}", openssl::check_request(pem.as_bytes()));
289 }
290
291 #[test]
certificate_request_attributes()292 fn certificate_request_attributes() {
293 let subject = Name::from_str("CN=service.domination.world").unwrap();
294
295 let signer = ecdsa_signer();
296 let mut builder = RequestBuilder::new(subject, &signer).expect("Create certificate request");
297 builder
298 .add_attribute(&request::attributes::ChallengePassword(
299 DirectoryString::PrintableString(
300 PrintableString::new(b"password1234")
301 .expect("create printable string with password"),
302 ),
303 ))
304 .expect("unable to add attribute");
305
306 let cert_req = builder.build::<DerSignature>().unwrap();
307 let pem = cert_req.to_pem(LineEnding::LF).expect("generate pem");
308 use std::fs::File;
309 use std::io::Write;
310 let mut file = File::create("/tmp/ecdsa.csr").expect("create pem file");
311 file.write_all(pem.as_bytes()).expect("Create pem file");
312 println!("{}", openssl::check_request(pem.as_bytes()));
313 }
314