Skip to content

Commit 9aa7cf7

Browse files
committed
examples: add writing and reading ca files
Signed-off-by: Joshua Potts <[email protected]>
1 parent 9f7fbb6 commit 9aa7cf7

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed

rcgen/examples/files.rs

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
use rcgen::{
2+
BasicConstraints, Certificate, CertificateParams, DnType, DnValue::PrintableString,
3+
ExtendedKeyUsagePurpose, IsCa, Issuer, KeyPair, KeyUsagePurpose,
4+
};
5+
use std::path::{Path, PathBuf};
6+
use time::{Duration, OffsetDateTime};
7+
8+
/// Generate a certificate chain, saving each step to filesystem and loading from filesystem for next step.
9+
fn main() {
10+
let output_dir = std::env::args()
11+
.nth(1)
12+
.map(PathBuf::from)
13+
.expect("provide output directory as first command line argument");
14+
15+
if !output_dir.exists() {
16+
panic!("output directory {} does not exist", output_dir.display());
17+
}
18+
19+
let ca_keys_file = output_dir.join("ca-keys.pem");
20+
let ca_cert_file = output_dir.join("ca-cert.pem");
21+
22+
let intermediate_keys_file = output_dir.join("intermediate-keys.pem");
23+
let intermediate_cert_file = output_dir.join("intermediate-cert.pem");
24+
25+
let server_keys_file = output_dir.join("server-keys.pem");
26+
let server_cert_file = output_dir.join("server-cert.pem");
27+
28+
write_new_ca(&ca_keys_file, &ca_cert_file);
29+
30+
write_new_intermediate_ca(
31+
&ca_keys_file,
32+
&ca_cert_file,
33+
&intermediate_keys_file,
34+
&intermediate_cert_file,
35+
);
36+
37+
write_new_server(
38+
&intermediate_keys_file,
39+
&intermediate_cert_file,
40+
&server_keys_file,
41+
&server_cert_file,
42+
);
43+
44+
println!(
45+
"Wrote root ca, intermediate ca, and leaf certificate to {}",
46+
output_dir.display()
47+
);
48+
println!();
49+
50+
#[cfg(unix)]
51+
{
52+
let verify_command = format!(
53+
"openssl verify -CAfile <(cat \"{}\" \"{}\") \"{}\"",
54+
ca_cert_file.display(),
55+
intermediate_cert_file.display(),
56+
server_cert_file.display()
57+
);
58+
59+
println!("To verify the certificate chain, run:");
60+
println!();
61+
println!(" {verify_command}");
62+
println!();
63+
}
64+
}
65+
66+
fn read_ca(keys_file: &Path, cert_file: &Path) -> Issuer<'static, KeyPair> {
67+
let keys_pem = std::fs::read_to_string(keys_file).expect("failed to read keys file");
68+
let cert_pem = std::fs::read_to_string(cert_file).expect("failed to read cert file");
69+
70+
let key_pair = KeyPair::from_pem(&keys_pem).expect("failed to parse keys file");
71+
72+
Issuer::from_ca_cert_pem(&cert_pem, key_pair).expect("failed to parse cert")
73+
}
74+
75+
fn write_cert(key_file: &Path, cert_file: &Path, key_pair: KeyPair, cert: Certificate) {
76+
std::fs::write(key_file, key_pair.serialize_pem()).expect("failed to write keys file");
77+
std::fs::write(cert_file, cert.pem()).expect("failed to write cert file");
78+
}
79+
80+
fn write_new_ca(key_file: &Path, cert_file: &Path) {
81+
let (key_pair, params) = new_unsigned_ca();
82+
83+
let cert = params.self_signed(&key_pair).unwrap();
84+
85+
write_cert(key_file, cert_file, key_pair, cert);
86+
}
87+
88+
fn new_unsigned_ca() -> (KeyPair, CertificateParams) {
89+
const NAME: &str = "Example Root CA";
90+
91+
let mut params = CertificateParams::new([]).unwrap();
92+
93+
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
94+
95+
params.distinguished_name.push(
96+
DnType::CountryName,
97+
PrintableString("BR".try_into().unwrap()),
98+
);
99+
params
100+
.distinguished_name
101+
.push(DnType::OrganizationName, "Crab widgits SE");
102+
params.distinguished_name.push(DnType::CommonName, NAME);
103+
104+
params.key_usages.push(KeyUsagePurpose::DigitalSignature);
105+
params.key_usages.push(KeyUsagePurpose::KeyCertSign);
106+
params.key_usages.push(KeyUsagePurpose::CrlSign);
107+
108+
let (yesterday, tomorrow) = validity_period();
109+
110+
params.not_before = yesterday;
111+
params.not_after = tomorrow;
112+
113+
let key_pair = KeyPair::generate().unwrap();
114+
115+
(key_pair, params)
116+
}
117+
118+
fn write_new_intermediate_ca(
119+
ca_key_file: &Path,
120+
ca_cert_file: &Path,
121+
intermediate_keys_file: &Path,
122+
intermediate_cert_file: &Path,
123+
) {
124+
let ca_issuer = read_ca(ca_key_file, ca_cert_file);
125+
126+
let (key_pair, params) = new_unsigned_intermediate_ca();
127+
let cert = params.signed_by(&key_pair, &ca_issuer).unwrap();
128+
129+
let keys_pem = key_pair.serialize_pem();
130+
let cert_pem = cert.pem();
131+
132+
std::fs::write(intermediate_keys_file, keys_pem)
133+
.expect("failed to write intermediate keys file");
134+
135+
std::fs::write(intermediate_cert_file, cert_pem)
136+
.expect("failed to write intermediate cert file");
137+
}
138+
139+
fn new_unsigned_intermediate_ca() -> (KeyPair, CertificateParams) {
140+
const NAME: &str = "Example Intermediate CA";
141+
142+
let mut params = CertificateParams::new([]).unwrap();
143+
144+
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
145+
146+
params.distinguished_name.push(
147+
DnType::CountryName,
148+
PrintableString("BR".try_into().unwrap()),
149+
);
150+
params
151+
.distinguished_name
152+
.push(DnType::OrganizationName, "Crab widgits SE");
153+
params.distinguished_name.push(DnType::CommonName, NAME);
154+
155+
params.key_usages.push(KeyUsagePurpose::DigitalSignature);
156+
params.key_usages.push(KeyUsagePurpose::KeyCertSign);
157+
params.key_usages.push(KeyUsagePurpose::CrlSign);
158+
159+
let (yesterday, tomorrow) = validity_period();
160+
161+
params.not_before = yesterday;
162+
params.not_after = tomorrow;
163+
164+
let key_pair = KeyPair::generate().unwrap();
165+
166+
(key_pair, params)
167+
}
168+
169+
fn write_new_server(
170+
intermediate_keys_file: &Path,
171+
intermediate_cert_file: &Path,
172+
keys_file: &Path,
173+
cert_file: &Path,
174+
) {
175+
let intermediate_issuer = read_ca(intermediate_keys_file, intermediate_cert_file);
176+
177+
let (key_pair, cert) = new_unsigned_server(&intermediate_issuer);
178+
179+
let keys_pem = key_pair.serialize_pem();
180+
let cert_pem = cert.pem();
181+
182+
std::fs::write(keys_file, keys_pem).expect("failed to write server keys file");
183+
std::fs::write(cert_file, cert_pem).expect("failed to write server cert file");
184+
}
185+
186+
fn new_unsigned_server(issuer: &Issuer<'static, KeyPair>) -> (KeyPair, Certificate) {
187+
const DOMAIN: &str = "example.domain";
188+
189+
let sans = vec![DOMAIN.into()];
190+
191+
let mut params = CertificateParams::new(sans).expect("invalid subject alt name");
192+
193+
params.distinguished_name.push(DnType::CommonName, DOMAIN);
194+
params.use_authority_key_identifier_extension = true;
195+
params.key_usages.push(KeyUsagePurpose::DigitalSignature);
196+
params
197+
.extended_key_usages
198+
.push(ExtendedKeyUsagePurpose::ServerAuth);
199+
200+
let (yesterday, tomorrow) = validity_period();
201+
202+
params.not_before = yesterday;
203+
params.not_after = tomorrow;
204+
205+
let key_pair = KeyPair::generate().unwrap();
206+
let cert = params.signed_by(&key_pair, issuer).unwrap();
207+
208+
(key_pair, cert)
209+
}
210+
211+
fn validity_period() -> (OffsetDateTime, OffsetDateTime) {
212+
const DAY: Duration = Duration::days(1);
213+
214+
let yesterday = OffsetDateTime::now_utc().checked_sub(DAY).unwrap();
215+
let tomorrow = OffsetDateTime::now_utc().checked_add(DAY).unwrap();
216+
217+
(yesterday, tomorrow)
218+
}

0 commit comments

Comments
 (0)