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