1use std::fs::File;
9use std::io::{Read, Write};
10use std::path::{Path, PathBuf};
11
12use tracing::{debug, error, info, trace, warn};
13
14use opcua_types::status_code::StatusCode;
15
16use crate::PrivateKey;
17
18use super::{
19 security_policy::SecurityPolicy,
20 x509::{X509Data, X509},
21};
22
23const OWN_CERTIFICATE_PATH: &str = "own/cert.der";
25const OWN_PRIVATE_KEY_PATH: &str = "private/private.pem";
27const TRUSTED_CERTS_DIR: &str = "trusted";
29const REJECTED_CERTS_DIR: &str = "rejected";
31
32pub struct CertificateStore {
35 own_certificate_path: PathBuf,
37 own_private_key_path: PathBuf,
39 pub(crate) pki_path: PathBuf,
41 check_time: bool,
44 skip_verify_certs: bool,
48 trust_unknown_certs: bool,
52}
53
54impl CertificateStore {
55 pub fn new(pki_path: &Path) -> CertificateStore {
59 CertificateStore {
60 own_certificate_path: PathBuf::from(OWN_CERTIFICATE_PATH),
61 own_private_key_path: PathBuf::from(OWN_PRIVATE_KEY_PATH),
62 pki_path: pki_path.to_path_buf(),
63 check_time: true,
64 skip_verify_certs: false,
65 trust_unknown_certs: false,
66 }
67 }
68
69 pub fn new_with_x509_data<X>(
72 pki_path: &Path,
73 overwrite: bool,
74 cert_path: Option<&Path>,
75 pkey_path: Option<&Path>,
76 x509_data: Option<X>,
77 ) -> (CertificateStore, Option<X509>, Option<PrivateKey>)
78 where
79 X: Into<X509Data>,
80 {
81 let mut certificate_store = CertificateStore::new(pki_path);
82 if let (Some(cert_path), Some(pkey_path)) = (cert_path, pkey_path) {
83 certificate_store.own_certificate_path = cert_path.to_path_buf();
84 certificate_store.own_private_key_path = pkey_path.to_path_buf();
85 }
86 let (cert, pkey) = if certificate_store.ensure_pki_path().is_err() {
87 error!("Folder for storing certificates cannot be examined so server has no application instance certificate or private key.");
88 (None, None)
89 } else {
90 let cert = certificate_store.read_own_cert();
91 let pkey = certificate_store.read_own_pkey();
92 match (cert, pkey, x509_data) {
93 (Ok(cert), Ok(pkey), _) => (Some(cert), Some(pkey)),
94 (_, _, Some(x509_data)) => {
95 info!("Creating sample application instance certificate and private key");
96 let x509_data = x509_data.into();
97 let result = certificate_store
98 .create_and_store_application_instance_cert(&x509_data, overwrite);
99 match result {
100 Ok((cert, pkey)) => (Some(cert), Some(pkey)),
101 Err(err) => {
102 error!("Certificate creation failed, error = {}", err);
103 (None, None)
104 }
105 }
106 }
107 (Err(e1), Err(e2), _) => {
108 error!("Failed to get cert and private key: {e1}, {e2}");
109 (None, None)
110 }
111 (Err(e), _, _) | (_, Err(e), _) => {
112 error!("Failed to get cert or private key: {e}");
113 (None, None)
114 }
115 }
116 };
117 (certificate_store, cert, pkey)
118 }
119
120 pub fn set_skip_verify_certs(&mut self, skip_verify_certs: bool) {
122 self.skip_verify_certs = skip_verify_certs;
123 }
124
125 pub fn set_trust_unknown_certs(&mut self, trust_unknown_certs: bool) {
128 self.trust_unknown_certs = trust_unknown_certs;
129 }
130
131 pub fn set_check_time(&mut self, check_time: bool) {
133 self.check_time = check_time;
134 }
135
136 pub fn read_pkey(path: &Path) -> Result<PrivateKey, String> {
138 if let Ok(pkey) = PrivateKey::read_pem_file(path) {
139 return Ok(pkey);
140 }
141
142 Err(format!("Cannot read pkey from path {path:?}"))
143 }
144
145 pub fn read_own_cert(&self) -> Result<X509, String> {
147 CertificateStore::read_cert(&self.own_certificate_path()).map_err(|e| {
148 format!(
149 "Cannot read cert from path {:?}: {e}",
150 self.own_certificate_path()
151 )
152 })
153 }
154
155 pub fn read_own_pkey(&self) -> Result<PrivateKey, String> {
157 CertificateStore::read_pkey(&self.own_private_key_path()).map_err(|e| {
158 format!(
159 "Cannot read pkey from path {:?}: {e}",
160 self.own_private_key_path()
161 )
162 })
163 }
164
165 pub fn create_certificate_and_key(
167 args: &X509Data,
168 overwrite: bool,
169 cert_path: &Path,
170 pkey_path: &Path,
171 ) -> Result<(X509, PrivateKey), String> {
172 let (cert, pkey) = X509::cert_and_pkey(args)?;
173
174 let _ = CertificateStore::store_cert(&cert, cert_path, overwrite)?;
176
177 use rsa::pkcs8;
179 use x509_cert::der::pem::PemLabel;
180 let doc = pkey.to_der().unwrap();
181 let pem = doc
182 .to_pem(rsa::pkcs8::PrivateKeyInfo::PEM_LABEL, pkcs8::LineEnding::CR)
183 .unwrap();
184 let _ = CertificateStore::write_to_file(pem.as_bytes(), pkey_path, overwrite)?;
185 Ok((cert, pkey))
186 }
187
188 pub fn create_and_store_application_instance_cert(
192 &self,
193 args: &X509Data,
194 overwrite: bool,
195 ) -> Result<(X509, PrivateKey), String> {
196 CertificateStore::create_certificate_and_key(
197 args,
198 overwrite,
199 &self.own_certificate_path(),
200 &self.own_private_key_path(),
201 )
202 }
203
204 pub fn validate_or_reject_application_instance_cert(
213 &self,
214 cert: &X509,
215 security_policy: SecurityPolicy,
216 hostname: Option<&str>,
217 application_uri: Option<&str>,
218 ) -> Result<(), StatusCode> {
219 self.validate_application_instance_cert(cert, security_policy, hostname, application_uri)
220 }
221
222 fn ensure_cert_and_file_are_the_same(cert: &X509, cert_path: &Path) -> bool {
227 if !cert_path.exists() {
228 trace!("Cannot find cert on disk");
229 false
230 } else {
231 match CertificateStore::read_cert(cert_path) {
232 Ok(file_der) => {
233 trace!("Comparing cert on disk to memory");
235 let der;
236 {
237 let r = cert.to_der();
238 match r {
239 Err(_) => return false,
240 Ok(val) => der = val,
241 }
242 }
243
244 let target_der;
245 {
246 let r = file_der.to_der();
247 match r {
248 Err(_) => return false,
249 Ok(val) => target_der = val,
250 }
251 }
252
253 der == target_der
254 }
255 Err(err) => {
256 trace!("Cannot read cert from disk {:?} - {}", cert_path, err);
257 false
259 }
260 }
261 }
262 }
263
264 pub fn validate_application_instance_cert(
275 &self,
276 cert: &X509,
277 security_policy: SecurityPolicy,
278 hostname: Option<&str>,
279 application_uri: Option<&str>,
280 ) -> Result<(), StatusCode> {
281 let cert_file_name = CertificateStore::cert_file_name(cert);
282 debug!("Validating cert with name on disk {}", cert_file_name);
283
284 {
287 let mut cert_path = self.rejected_certs_dir();
288 if !cert_path.exists() {
289 error!(
290 "Path for rejected certificates {} does not exist",
291 cert_path.display()
292 );
293 return Err(StatusCode::BadUnexpectedError);
294 }
295 cert_path.push(&cert_file_name);
296 if cert_path.exists() {
297 warn!(
298 "Certificate {} is untrusted because it resides in the rejected directory",
299 cert_file_name
300 );
301 return Err(StatusCode::BadSecurityChecksFailed);
302 }
303 }
304
305 {
308 let mut cert_path = self.trusted_certs_dir();
310 if !cert_path.exists() {
311 error!(
312 "Path for rejected certificates {} does not exist",
313 cert_path.display()
314 );
315 return Err(StatusCode::BadUnexpectedError);
316 }
317 cert_path.push(&cert_file_name);
318
319 if !cert_path.exists() {
321 if self.trust_unknown_certs {
323 warn!("Certificate {} is unknown but policy will store it into the trusted directory", cert_file_name);
325 let _ = self.store_trusted_cert(cert);
326 } else {
328 warn!("Certificate {} is unknown and untrusted so it will be stored in rejected directory", cert_file_name);
329 let _ = self.store_rejected_cert(cert);
330 return Err(StatusCode::BadCertificateUntrusted);
331 }
332 }
333
334 if !CertificateStore::ensure_cert_and_file_are_the_same(cert, &cert_path) {
336 error!("Certificate in memory does not match the one on disk {} so cert will automatically be treated as untrusted", cert_path.display());
337 return Err(StatusCode::BadUnexpectedError);
338 }
339
340 match cert.key_length() {
342 Err(_) => {
343 error!("Cannot read key length from certificate {}", cert_file_name);
344 return Err(StatusCode::BadSecurityChecksFailed);
345 }
346 Ok(key_length) => {
347 if !security_policy.is_valid_keylength(key_length) {
348 warn!(
349 "Certificate {} has an invalid key length {} for the policy {}",
350 cert_file_name, key_length, security_policy
351 );
352 return Err(StatusCode::BadSecurityChecksFailed);
353 }
354 }
355 }
356
357 if self.skip_verify_certs {
358 debug!(
359 "Skipping additional verifications for certificate {}",
360 cert_file_name
361 );
362 return Ok(());
363 }
364
365 if self.check_time {
367 use chrono::Utc;
368 let now = Utc::now();
369 cert.is_time_valid(&now)?;
370 }
371
372 if let Some(hostname) = hostname {
374 cert.is_hostname_valid(hostname)?;
375 }
376
377 if let Some(application_uri) = application_uri {
379 cert.is_application_uri_valid(application_uri)?;
380 }
381
382 }
387 Ok(())
388 }
389
390 pub fn cert_file_name(cert: &X509) -> String {
394 let prefix = if let Ok(common_name) = cert.common_name() {
395 common_name.trim().to_string().replace('/', "")
396 } else {
397 String::new()
398 };
399 let thumbprint = cert.thumbprint().as_hex_string();
400
401 if !prefix.is_empty() {
402 format!("{prefix} [{thumbprint}].der")
403 } else {
404 format!("{thumbprint}.der")
405 }
406 }
407
408 pub fn ensure_pki_path(&self) -> Result<(), String> {
415 let mut path = self.pki_path.clone();
416 let subdirs = [TRUSTED_CERTS_DIR, REJECTED_CERTS_DIR];
417 for subdir in &subdirs {
418 path.push(subdir);
419 CertificateStore::ensure_dir(&path)?;
420 path.pop();
421 }
422 Ok(())
423 }
424
425 fn ensure_dir(path: &Path) -> Result<(), String> {
432 if path.exists() {
433 if !path.is_dir() {
434 Err(format!("{} is not a directory ", path.display()))
435 } else {
436 Ok(())
437 }
438 } else {
439 std::fs::create_dir_all(path)
440 .map_err(|_| format!("Cannot make directories for {}", path.display()))
441 }
442 }
443
444 pub fn own_certificate_path(&self) -> PathBuf {
446 let mut path = PathBuf::from(&self.pki_path);
447 path.push(&self.own_certificate_path);
448 path
449 }
450
451 pub fn own_private_key_path(&self) -> PathBuf {
453 let mut path = PathBuf::from(&self.pki_path);
454 path.push(&self.own_private_key_path);
455 path
456 }
457
458 pub fn rejected_certs_dir(&self) -> PathBuf {
460 let mut path = PathBuf::from(&self.pki_path);
461 path.push(REJECTED_CERTS_DIR);
462 path
463 }
464
465 pub fn trusted_certs_dir(&self) -> PathBuf {
467 let mut path = PathBuf::from(&self.pki_path);
468 path.push(TRUSTED_CERTS_DIR);
469 path
470 }
471
472 pub fn store_rejected_cert(&self, cert: &X509) -> Result<PathBuf, String> {
480 let cert_file_name = CertificateStore::cert_file_name(cert);
482 let mut cert_path = self.rejected_certs_dir();
483 cert_path.push(&cert_file_name);
484 let _ = CertificateStore::store_cert(cert, &cert_path, true)?;
485 Ok(cert_path)
486 }
487
488 fn store_trusted_cert(&self, cert: &X509) -> Result<PathBuf, String> {
496 let cert_file_name = CertificateStore::cert_file_name(cert);
498 let mut cert_path = self.trusted_certs_dir();
499 cert_path.push(&cert_file_name);
500 let _ = CertificateStore::store_cert(cert, &cert_path, true)?;
501 Ok(cert_path)
502 }
503
504 fn store_cert(cert: &X509, path: &Path, overwrite: bool) -> Result<usize, String> {
511 let der = cert.to_der().unwrap();
512 info!("Writing X509 cert to {}", path.display());
513 CertificateStore::write_to_file(&der, path, overwrite)
514 }
515
516 pub fn read_cert(path: &Path) -> Result<X509, String> {
523 let file = File::open(path);
524 if file.is_err() {
525 return Err(format!("Could not open cert file {}", path.display()));
526 }
527
528 let mut file: File = file.unwrap();
529 let mut cert = Vec::new();
530 let bytes_read = file.read_to_end(&mut cert);
531 if bytes_read.is_err() {
532 return Err(format!(
533 "Could not read bytes from cert file {}",
534 path.display()
535 ));
536 }
537
538 let cert = match path.extension() {
539 Some(v) if v == "der" => X509::from_der(&cert),
540 Some(v) if v == "pem" => X509::from_pem(&cert),
541 _ => return Err("Only .der and .pem certificates are supported".to_string()),
542 };
543
544 match cert {
545 Err(_) => Err(format!(
546 "Could not read cert from cert file {}",
547 path.display()
548 )),
549 Ok(val) => Ok(val),
550 }
551 }
552
553 fn write_to_file(bytes: &[u8], file_path: &Path, overwrite: bool) -> Result<usize, String> {
560 if !overwrite && file_path.exists() {
561 Err(format!("File {} already exists and will not be overwritten. Enable overwrite to disable this safeguard.", file_path.display()))
562 } else {
563 if let Some(parent) = file_path.parent() {
564 CertificateStore::ensure_dir(parent)?;
565 }
566 match File::create(file_path) {
567 Ok(mut file) => file
568 .write(bytes)
569 .map_err(|_| format!("Could not write bytes to file {}", file_path.display())),
570 Err(_) => Err(format!("Could not create file {}", file_path.display())),
571 }
572 }
573 }
574}