Skip to main content

prosa_utils/config/ssl/
openssl.rs

1//! Definition of [OpenSSL](https://www.openssl.org/) configuration
2//!
3//! Implement:
4//! - [`SslStore`](https://docs.rs/prosa-utils/latest/prosa_utils/config/ssl/trait.SslStore.html#impl-SslStore%3CX509,+X509Store%3E-for-Store)
5//! - [`SslConfigContext`](https://docs.rs/prosa-utils/latest/prosa_utils/config/ssl/trait.SslConfigContext.html#impl-SslConfigContext%3CSslConnectorBuilder,+SslAcceptorBuilder%3E-for-SslConfig)
6
7use std::{
8    collections::HashMap,
9    ffi::OsStr,
10    fs::{self, File},
11    io::Write as _,
12    net::IpAddr,
13    num::TryFromIntError,
14    ops::DerefMut,
15    time,
16};
17
18use bytes::{BufMut as _, BytesMut};
19use openssl::{
20    asn1::{Asn1Integer, Asn1Time},
21    bn::{BigNum, MsbOption},
22    ec::{Asn1Flag, EcGroup, EcKey},
23    hash::MessageDigest,
24    nid::Nid,
25    pkey::PKey,
26    ssl::{
27        AlpnError, SslAcceptor, SslAcceptorBuilder, SslConnectorBuilder, SslContextBuilder,
28        SslFiletype, SslMethod, SslVerifyMode,
29    },
30    x509::{
31        X509, X509NameBuilder,
32        extension::SubjectAlternativeName,
33        store::{X509Store, X509StoreBuilder},
34    },
35};
36
37use crate::config::{
38    ConfigError, os_country,
39    ssl::{SslConfig, SslConfigContext, SslStore, Store},
40};
41
42impl SslStore<X509, X509Store> for Store {
43    fn get_file_certificates(path: &std::path::Path) -> Result<Vec<X509>, ConfigError> {
44        if path.is_file() {
45            match &path.extension().and_then(OsStr::to_str) {
46                Some("pem" | "crt") => match fs::read(path) {
47                    Ok(pem_file) => Ok(vec![X509::from_pem(&pem_file)?]),
48                    Err(io) => Err(ConfigError::IoFile(
49                        path.to_str().unwrap_or_default().into(),
50                        io,
51                    )),
52                },
53                Some("der" | "cer") => match fs::read(path) {
54                    Ok(der_file) => Ok(vec![X509::from_der(&der_file)?]),
55                    Err(io) => Err(ConfigError::IoFile(
56                        path.to_str().unwrap_or_default().into(),
57                        io,
58                    )),
59                },
60                _ => Ok(Vec::new()),
61            }
62        } else if path.is_symlink() {
63            if let Ok(link) = path.read_link() {
64                <Self as SslStore<X509, X509Store>>::get_file_certificates(&link)
65            } else {
66                Ok(Vec::new())
67            }
68        } else if let Ok(path_dir) = path.read_dir() {
69            let mut cert_list = Vec::new();
70            for dir_entry in path_dir.flatten() {
71                cert_list.append(
72                    &mut <Self as SslStore<X509, X509Store>>::get_file_certificates(
73                        &dir_entry.path(),
74                    )?,
75                );
76            }
77
78            Ok(cert_list)
79        } else {
80            Ok(Vec::new())
81        }
82    }
83
84    /// Method to get an OpenSSL cert store
85    ///
86    /// ```
87    /// use prosa_utils::config::ssl::{Store, SslStore};
88    ///
89    /// let store = Store::File { path: "./target".into() };
90    /// let openssl_store: openssl::x509::store::X509Store = store.get_store().unwrap();
91    /// ```
92    fn get_store(&self) -> Result<X509Store, ConfigError> {
93        let mut store = X509StoreBuilder::new()?;
94        match self {
95            Store::System => {
96                store.set_default_paths()?;
97                Ok(store.build())
98            }
99            Store::File { path } => match glob::glob(path) {
100                Ok(certs) => {
101                    for cert_path in certs.flatten() {
102                        for cert in
103                            <Self as SslStore<X509, X509Store>>::get_file_certificates(&cert_path)?
104                        {
105                            store.add_cert(cert)?;
106                        }
107                    }
108
109                    Ok(store.build())
110                }
111                Err(e) => Err(ConfigError::WrongPathPattern(path.clone(), e)),
112            },
113            Store::Cert { certs } => {
114                for cert in certs {
115                    store.add_cert(X509::from_pem(cert.as_bytes())?)?;
116                }
117
118                Ok(store.build())
119            }
120        }
121    }
122
123    /// Method to get all OpenSSL certificate with their names as key
124    ///
125    /// ```
126    /// use std::collections::HashMap;
127    /// use openssl::x509::X509;
128    /// use prosa_utils::config::ssl::{Store, SslStore};
129    ///
130    /// let store = Store::File { path: "./target".into() };
131    /// let certs_map: HashMap<String, X509> = store.get_certs().unwrap();
132    ///
133    /// // No cert in target
134    /// assert!(certs_map.is_empty());
135    /// ```
136    fn get_certs(&self) -> Result<HashMap<String, X509>, ConfigError> {
137        match self {
138            Store::System => {
139                #[cfg(ossl300)]
140                {
141                    let store: X509Store = self.get_store()?;
142                    let mut certs_map = HashMap::new();
143
144                    for cert in store.all_certificates() {
145                        if let Some(name) = cert
146                            .subject_name()
147                            .entries_by_nid(Nid::COMMONNAME)
148                            .last()
149                            .and_then(|cn| cn.data().as_utf8().map(|cn| cn.to_string()).ok())
150                        {
151                            certs_map.insert(name, cert);
152                        }
153                    }
154
155                    Ok(certs_map)
156                }
157
158                #[cfg(not(ossl300))]
159                Ok(HashMap::new())
160            }
161            Store::File { path } => match glob::glob(path) {
162                Ok(certs) => {
163                    let mut certs_map = HashMap::new();
164                    for cert_path in certs.flatten() {
165                        let certs =
166                            <Self as SslStore<X509, X509Store>>::get_file_certificates(&cert_path)?;
167                        for cert in certs {
168                            if let Some(name) = cert
169                                .subject_name()
170                                .entries_by_nid(Nid::COMMONNAME)
171                                .last()
172                                .and_then(|cn| cn.data().as_utf8().map(|cn| cn.to_string()).ok())
173                            {
174                                certs_map.insert(name, cert);
175                            } else if let Some(cert_name) = cert_path.to_str().and_then(|p| {
176                                p.strip_suffix(".pem").or(p
177                                    .strip_suffix(".crt")
178                                    .or(p.strip_suffix(".der").or(p.strip_suffix(".cer"))))
179                            }) {
180                                certs_map.insert(cert_name.into(), cert);
181                            }
182                        }
183                    }
184
185                    Ok(certs_map)
186                }
187                Err(e) => Err(ConfigError::WrongPathPattern(path.clone(), e)),
188            },
189            Store::Cert { certs } => {
190                let mut certs_map = HashMap::new();
191                for cert_pem in certs {
192                    let cert = X509::from_pem(cert_pem.as_bytes())?;
193                    if let Some(name) = cert
194                        .subject_name()
195                        .entries_by_nid(Nid::COMMONNAME)
196                        .last()
197                        .and_then(|cn| cn.data().as_utf8().map(|cn| cn.to_string()).ok())
198                    {
199                        certs_map.insert(name, cert);
200                    }
201                }
202
203                Ok(certs_map)
204            }
205        }
206    }
207}
208
209/// Method to init an SSL context for a socket
210pub(crate) fn init_openssl_context<B>(
211    config: &SslConfig,
212    mut context_builder: B,
213    is_server: bool,
214    host: Option<&str>,
215) -> Result<B, ConfigError>
216where
217    B: DerefMut<Target = SslContextBuilder>,
218{
219    if let Some(pkcs12_path) = &config.pkcs12 {
220        match fs::read(pkcs12_path) {
221            Ok(pkcs12_file) => {
222                let pkcs12 = ::openssl::pkcs12::Pkcs12::from_der(pkcs12_file.as_ref())?
223                    .parse2(config.passphrase.as_ref().unwrap_or(&String::from("")))?;
224
225                if let Some(pkey) = pkcs12.pkey {
226                    context_builder.set_private_key(&pkey)?;
227                }
228
229                if let Some(cert) = pkcs12.cert {
230                    context_builder.set_certificate(&cert)?;
231                }
232
233                if let Some(ca) = pkcs12.ca {
234                    for cert in ca {
235                        context_builder.add_extra_chain_cert(cert)?;
236                    }
237                }
238            }
239            Err(io) => return Err(ConfigError::IoFile(pkcs12_path.to_string(), io)),
240        }
241    } else if let (Some(cert_path), Some(key_path)) = (&config.cert, &config.key) {
242        context_builder.set_certificate_file(cert_path, SslFiletype::PEM)?;
243
244        match fs::read(key_path) {
245            Ok(key_file) => {
246                let pkey = if key_path.ends_with(".der") || key_path.ends_with(".cer") {
247                    PKey::private_key_from_der(key_file.as_slice())?
248                } else if let Some(passphrase) = &config.passphrase {
249                    PKey::private_key_from_pem_passphrase(
250                        key_file.as_slice(),
251                        passphrase.as_bytes(),
252                    )?
253                } else {
254                    PKey::private_key_from_pem(key_file.as_slice())?
255                };
256
257                context_builder.set_private_key(&pkey)?;
258            }
259            Err(io) => return Err(ConfigError::IoFile(key_path.to_string(), io)),
260        }
261    } else if is_server {
262        let mut group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)?;
263        group.set_asn1_flag(Asn1Flag::NAMED_CURVE);
264        let pkey = PKey::from_ec_key(EcKey::generate(&group)?)?;
265        context_builder.set_private_key(&pkey)?;
266
267        let mut cert = X509::builder()?;
268        cert.set_version(2)?;
269        cert.set_pubkey(&pkey)?;
270
271        let mut serial_bn = BigNum::new()?;
272        serial_bn.pseudo_rand(64, MsbOption::MAYBE_ZERO, true)?;
273        let serial_number = Asn1Integer::from_bn(&serial_bn)?;
274        cert.set_serial_number(&serial_number)?;
275
276        let begin_valid_time = Asn1Time::from_unix(
277            (time::UNIX_EPOCH
278                .elapsed()
279                .map_err(|e| {
280                    ConfigError::WrongValue("time::UNIX_EPOCH".to_string(), e.to_string())
281                })?
282                .as_secs()
283                - 360)
284                .try_into()
285                .map_err(|e: TryFromIntError| {
286                    ConfigError::WrongValue("Asn1Time::from_UNIX_EPOCH".to_string(), e.to_string())
287                })?,
288        )?;
289        cert.set_not_before(&begin_valid_time)?;
290        let end_valid_time = Asn1Time::days_from_now(1461)?; // 4 years from now
291        cert.set_not_after(&end_valid_time)?;
292
293        let mut x509_name = X509NameBuilder::new()?;
294        if let Some(cn) = os_country() {
295            x509_name.append_entry_by_text("C", cn.as_str())?;
296        }
297        x509_name.append_entry_by_text("CN", "ProSA")?;
298        let x509_name = x509_name.build();
299        cert.set_subject_name(&x509_name)?;
300        cert.set_issuer_name(&x509_name)?;
301
302        // Add DNS or IP subject alternative name if needed to check the certificate
303        if let Some(host) = host {
304            if let Ok(ip) = host.parse::<IpAddr>() {
305                if !ip.is_unspecified() && !ip.is_loopback() {
306                    let mut subject_alternative_name = SubjectAlternativeName::new();
307                    let x509_extension = subject_alternative_name
308                        .ip(host)
309                        .build(&cert.x509v3_context(None, None))?;
310                    cert.append_extension2(&x509_extension)?;
311                }
312            } else {
313                let mut subject_alternative_name = SubjectAlternativeName::new();
314                let x509_extension = subject_alternative_name
315                    .dns(host)
316                    .build(&cert.x509v3_context(None, None))?;
317                cert.append_extension2(&x509_extension)?;
318            }
319        }
320
321        cert.sign(&pkey, MessageDigest::sha256())?;
322        let cert_x509 = cert.build();
323
324        // Write out the certificate if a path for it is specify
325        if let Some(cert_path) = &config.cert {
326            let mut cert_file =
327                File::create(cert_path).map_err(|e| ConfigError::IoFile(cert_path.clone(), e))?;
328            if cert_path.ends_with(".der") || cert_path.ends_with(".cer") {
329                cert_file
330                    .write_all(&cert_x509.to_der()?)
331                    .map_err(|e| ConfigError::IoFile(cert_path.clone(), e))?;
332            } else {
333                cert_file
334                    .write_all(&cert_x509.to_pem()?)
335                    .map_err(|e| ConfigError::IoFile(cert_path.clone(), e))?;
336            }
337        }
338
339        context_builder.set_certificate(&cert_x509)?;
340    }
341
342    if let Some(store) = &config.store {
343        context_builder.set_cert_store(store.get_store()?);
344        if is_server {
345            context_builder.set_verify(SslVerifyMode::PEER);
346        }
347    } else if !is_server {
348        context_builder.set_cert_store(Store::default().get_store()?);
349    } else {
350        context_builder.set_verify(SslVerifyMode::NONE);
351    }
352
353    if !config.alpn.is_empty() {
354        if is_server {
355            let alpn_list = config.alpn.clone();
356            context_builder.set_alpn_select_callback(move |_ssl, alpn| {
357                let mut cli_alpn = HashMap::new();
358
359                let mut current_split = alpn;
360                while let Some(length) = current_split.first() {
361                    if current_split.len() > *length as usize
362                        && let (left, right) = current_split.split_at(*length as usize + 1)
363                        && let Ok(alpn_key) = String::from_utf8(left[1..].to_vec())
364                    {
365                        cli_alpn.insert(alpn_key, &left[1..]);
366                        current_split = right;
367                    } else {
368                        return Err(AlpnError::ALERT_FATAL);
369                    }
370                }
371
372                for alpn_name in &alpn_list {
373                    if let Some(alpn) = cli_alpn.get(alpn_name) {
374                        return Ok(alpn);
375                    }
376                }
377
378                Err(AlpnError::NOACK)
379            });
380        } else {
381            let mut alpn_bytes = BytesMut::new();
382            for alpn in &config.alpn {
383                alpn_bytes.put_u8(alpn.len() as u8);
384                alpn_bytes.put(alpn.as_bytes());
385            }
386
387            context_builder.set_alpn_protos(&alpn_bytes)?;
388        }
389    }
390
391    Ok(context_builder)
392}
393
394impl SslConfigContext<SslConnectorBuilder, SslAcceptorBuilder> for SslConfig {
395    /// Method to init an OpenSSL context for a client socket
396    ///
397    /// ```
398    /// use std::io;
399    /// use std::pin::Pin;
400    /// use tokio::net::TcpStream;
401    /// use tokio_openssl::SslStream;
402    /// use openssl::ssl::{ErrorCode, Ssl, SslMethod, SslVerifyMode};
403    /// use prosa_utils::config::ssl::{SslConfig, SslConfigContext};
404    ///
405    /// async fn client() -> Result<(), io::Error> {
406    ///     let mut stream = TcpStream::connect("localhost:4443").await?;
407    ///
408    ///     let client_config = SslConfig::default();
409    ///     if let Ok(mut ssl_context_builder) = client_config.init_tls_client_context() {
410    ///         let ssl_context = ssl_context_builder.build();
411    ///         let ssl = Ssl::new(&ssl_context.context()).unwrap();
412    ///         let mut stream = SslStream::new(ssl, stream).unwrap();
413    ///         if let Err(e) = Pin::new(&mut stream).connect().await {
414    ///             if e.code() != ErrorCode::ZERO_RETURN {
415    ///                 eprintln!("Can't connect the client: {}", e);
416    ///             }
417    ///         }
418    ///
419    ///         // SSL stream ...
420    ///     }
421    ///
422    ///     Ok(())
423    /// }
424    /// ```
425    fn init_tls_client_context(&self) -> Result<openssl::ssl::SslConnectorBuilder, ConfigError> {
426        let mut ssl_connector = openssl::ssl::SslConnector::builder(SslMethod::tls_client())?;
427        init_openssl_context(self, ssl_connector.deref_mut(), false, None)?;
428        Ok(ssl_connector)
429    }
430
431    /// Method to init an OpenSSL context for a server socket
432    ///
433    /// ```
434    /// use std::io;
435    /// use std::pin::Pin;
436    /// use tokio::net::TcpListener;
437    /// use tokio_openssl::SslStream;
438    /// use openssl::ssl::{ErrorCode, Ssl, SslMethod, SslVerifyMode};
439    /// use prosa_utils::config::ssl::{SslConfig, SslConfigContext};
440    ///
441    /// async fn server() -> Result<(), io::Error> {
442    ///     let listener = TcpListener::bind("0.0.0.0:4443").await?;
443    ///
444    ///     let server_config = SslConfig::new_cert_key("cert.pem".into(), "cert.key".into(), Some("passphrase".into()));
445    ///     if let Ok(mut ssl_context_builder) = server_config.init_tls_server_context(Some("localhost")) {
446    ///         ssl_context_builder.set_verify(SslVerifyMode::NONE);
447    ///         let ssl_context = ssl_context_builder.build();
448    ///
449    ///         loop {
450    ///             let (stream, cli_addr) = listener.accept().await?;
451    ///             let ssl = Ssl::new(&ssl_context.context()).unwrap();
452    ///             let mut stream = SslStream::new(ssl, stream).unwrap();
453    ///             if let Err(e) = Pin::new(&mut stream).accept().await {
454    ///                 if e.code() != ErrorCode::ZERO_RETURN {
455    ///                     eprintln!("Can't accept the client {}: {}", cli_addr, e);
456    ///                 }
457    ///             }
458    ///
459    ///             // SSL stream ...
460    ///         }
461    ///     }
462    ///
463    ///     Ok(())
464    /// }
465    /// ```
466    fn init_tls_server_context(
467        &self,
468        host: Option<&str>,
469    ) -> Result<openssl::ssl::SslAcceptorBuilder, ConfigError> {
470        let mut ssl_acceptor = SslAcceptor::mozilla_modern(SslMethod::tls_server())?;
471        init_openssl_context(self, ssl_acceptor.deref_mut(), true, host)?;
472        Ok(ssl_acceptor)
473    }
474}
475
476#[cfg(test)]
477mod tests {
478    use super::*;
479
480    #[test]
481    fn test_store() {
482        let store_le_x1_x2 = Store::Cert {
483            certs: vec![
484                "-----BEGIN CERTIFICATE-----
485MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
486TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
487cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
488WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
489ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
490MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
491h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
4920TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
493A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
494T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
495B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
496B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
497KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
498OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
499jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
500qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
501rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
502HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
503hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
504ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
5053BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
506NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
507ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
508TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
509jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
510oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
5114RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
512mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
513emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
514-----END CERTIFICATE-----"
515                    .to_string(),
516                "-----BEGIN CERTIFICATE-----
517MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
518CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
519R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
520MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
521ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
522EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
523+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
524ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
525AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
526zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
527tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
528/q4AaOeMSQ+2b1tbFfLn
529-----END CERTIFICATE-----"
530                    .to_string(),
531            ],
532        };
533
534        let certs: HashMap<String, ::openssl::x509::X509> = store_le_x1_x2
535            .get_certs()
536            .expect("Certificates should be retrieved");
537        assert!(!certs.is_empty());
538
539        let ossl_store: ::openssl::x509::store::X509Store = store_le_x1_x2
540            .get_store()
541            .expect("Certificates store should be retrieved");
542        assert!(!ossl_store.all_certificates().is_empty())
543    }
544
545    #[cfg(ossl300)]
546    #[test]
547    fn test_system_store() {
548        let certs_map: HashMap<String, X509> = Store::System.get_certs().unwrap();
549        assert!(!certs_map.is_empty());
550    }
551
552    #[test]
553    fn test_tls_server_context() {
554        let ssl_config = SslConfig::default();
555        let ssl_acceptor_builder: ::openssl::ssl::SslAcceptorBuilder = ssl_config
556            .init_tls_server_context(None)
557            .expect("The TLS server context should be init");
558        let ssl_acceptor = ssl_acceptor_builder.build();
559
560        // Check for self signed certificate
561        assert!(ssl_acceptor.context().private_key().is_some());
562        assert!(ssl_acceptor.context().certificate().is_some());
563    }
564}