reqwest/
tls.rs

1//! TLS configuration and types
2//!
3//! A `Client` will use transport layer security (TLS) by default to connect to
4//! HTTPS destinations.
5//!
6//! # Backends
7//!
8//! reqwest supports several TLS backends, enabled with Cargo features.
9//!
10//! ## default-tls
11//!
12//! reqwest will pick a TLS backend by default. This is true when the
13//! `default-tls` feature is enabled.
14//!
15//! While it currently uses `native-tls`, the feature set is designed to only
16//! enable configuration that is shared among available backends. This allows
17//! reqwest to change the default to `rustls` (or another) at some point in the
18//! future.
19//!
20//! <div class="warning">This feature is enabled by default, and takes
21//! precedence if any other crate enables it. This is true even if you declare
22//! `features = []`. You must set `default-features = false` instead.</div>
23//!
24//! Since Cargo features are additive, other crates in your dependency tree can
25//! cause the default backend to be enabled. If you wish to ensure your
26//! `Client` uses a specific backend, call the appropriate builder methods
27//! (such as [`use_rustls_tls()`][]).
28//!
29//! [`use_rustls_tls()`]: crate::ClientBuilder::use_rustls_tls()
30//!
31//! ## native-tls
32//!
33//! This backend uses the [native-tls][] crate. That will try to use the system
34//! TLS on Windows and Mac, and OpenSSL on Linux targets.
35//!
36//! Enabling the feature explicitly allows for `native-tls`-specific
37//! configuration options.
38//!
39//! [native-tls]: https://crates.io/crates/native-tls
40//!
41//! ## rustls-tls
42//!
43//! This backend uses the [rustls][] crate, a TLS library written in Rust.
44//!
45//! [rustls]: https://crates.io/crates/rustls
46
47#[cfg(feature = "__rustls")]
48use rustls::{
49    client::HandshakeSignatureValid, client::ServerCertVerified, client::ServerCertVerifier,
50    DigitallySignedStruct, Error as TLSError, ServerName,
51};
52use std::{
53    fmt,
54    io::{BufRead, BufReader},
55};
56
57/// Represents a server X509 certificate.
58#[derive(Clone)]
59pub struct Certificate {
60    #[cfg(feature = "native-tls-crate")]
61    native: native_tls_crate::Certificate,
62    #[cfg(feature = "__rustls")]
63    original: Cert,
64}
65
66#[cfg(feature = "__rustls")]
67#[derive(Clone)]
68enum Cert {
69    Der(Vec<u8>),
70    Pem(Vec<u8>),
71}
72
73/// Represents a private key and X509 cert as a client certificate.
74#[derive(Clone)]
75pub struct Identity {
76    #[cfg_attr(not(any(feature = "native-tls", feature = "__rustls")), allow(unused))]
77    inner: ClientCert,
78}
79
80#[derive(Clone)]
81enum ClientCert {
82    #[cfg(feature = "native-tls")]
83    Pkcs12(native_tls_crate::Identity),
84    #[cfg(feature = "native-tls")]
85    Pkcs8(native_tls_crate::Identity),
86    #[cfg(feature = "__rustls")]
87    Pem {
88        key: rustls::PrivateKey,
89        certs: Vec<rustls::Certificate>,
90    },
91}
92
93impl Certificate {
94    /// Create a `Certificate` from a binary DER encoded certificate
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// # use std::fs::File;
100    /// # use std::io::Read;
101    /// # fn cert() -> Result<(), Box<dyn std::error::Error>> {
102    /// let mut buf = Vec::new();
103    /// File::open("my_cert.der")?
104    ///     .read_to_end(&mut buf)?;
105    /// let cert = reqwest::Certificate::from_der(&buf)?;
106    /// # drop(cert);
107    /// # Ok(())
108    /// # }
109    /// ```
110    pub fn from_der(der: &[u8]) -> crate::Result<Certificate> {
111        Ok(Certificate {
112            #[cfg(feature = "native-tls-crate")]
113            native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?,
114            #[cfg(feature = "__rustls")]
115            original: Cert::Der(der.to_owned()),
116        })
117    }
118
119    /// Create a `Certificate` from a PEM encoded certificate
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// # use std::fs::File;
125    /// # use std::io::Read;
126    /// # fn cert() -> Result<(), Box<dyn std::error::Error>> {
127    /// let mut buf = Vec::new();
128    /// File::open("my_cert.pem")?
129    ///     .read_to_end(&mut buf)?;
130    /// let cert = reqwest::Certificate::from_pem(&buf)?;
131    /// # drop(cert);
132    /// # Ok(())
133    /// # }
134    /// ```
135    pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> {
136        Ok(Certificate {
137            #[cfg(feature = "native-tls-crate")]
138            native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?,
139            #[cfg(feature = "__rustls")]
140            original: Cert::Pem(pem.to_owned()),
141        })
142    }
143
144    /// Create a collection of `Certificate`s from a PEM encoded certificate bundle.
145    /// Example byte sources may be `.crt`, `.cer` or `.pem` files.
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// # use std::fs::File;
151    /// # use std::io::Read;
152    /// # fn cert() -> Result<(), Box<dyn std::error::Error>> {
153    /// let mut buf = Vec::new();
154    /// File::open("ca-bundle.crt")?
155    ///     .read_to_end(&mut buf)?;
156    /// let certs = reqwest::Certificate::from_pem_bundle(&buf)?;
157    /// # drop(certs);
158    /// # Ok(())
159    /// # }
160    /// ```
161    pub fn from_pem_bundle(pem_bundle: &[u8]) -> crate::Result<Vec<Certificate>> {
162        let mut reader = BufReader::new(pem_bundle);
163
164        Self::read_pem_certs(&mut reader)?
165            .iter()
166            .map(|cert_vec| Certificate::from_der(&cert_vec))
167            .collect::<crate::Result<Vec<Certificate>>>()
168    }
169
170    #[cfg(feature = "native-tls-crate")]
171    pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) {
172        tls.add_root_certificate(self.native);
173    }
174
175    #[cfg(feature = "__rustls")]
176    pub(crate) fn add_to_rustls(
177        self,
178        root_cert_store: &mut rustls::RootCertStore,
179    ) -> crate::Result<()> {
180        use std::io::Cursor;
181
182        match self.original {
183            Cert::Der(buf) => root_cert_store
184                .add(&rustls::Certificate(buf))
185                .map_err(crate::error::builder)?,
186            Cert::Pem(buf) => {
187                let mut reader = Cursor::new(buf);
188                let certs = Self::read_pem_certs(&mut reader)?;
189                for c in certs {
190                    root_cert_store
191                        .add(&rustls::Certificate(c))
192                        .map_err(crate::error::builder)?;
193                }
194            }
195        }
196        Ok(())
197    }
198
199    fn read_pem_certs(reader: &mut impl BufRead) -> crate::Result<Vec<Vec<u8>>> {
200        rustls_pemfile::certs(reader)
201            .map_err(|_| crate::error::builder("invalid certificate encoding"))
202    }
203}
204
205impl Identity {
206    /// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key.
207    ///
208    /// The archive should contain a leaf certificate and its private key, as well any intermediate
209    /// certificates that allow clients to build a chain to a trusted root.
210    /// The chain certificates should be in order from the leaf certificate towards the root.
211    ///
212    /// PKCS #12 archives typically have the file extension `.p12` or `.pfx`, and can be created
213    /// with the OpenSSL `pkcs12` tool:
214    ///
215    /// ```bash
216    /// openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem
217    /// ```
218    ///
219    /// # Examples
220    ///
221    /// ```
222    /// # use std::fs::File;
223    /// # use std::io::Read;
224    /// # fn pkcs12() -> Result<(), Box<dyn std::error::Error>> {
225    /// let mut buf = Vec::new();
226    /// File::open("my-ident.pfx")?
227    ///     .read_to_end(&mut buf)?;
228    /// let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?;
229    /// # drop(pkcs12);
230    /// # Ok(())
231    /// # }
232    /// ```
233    ///
234    /// # Optional
235    ///
236    /// This requires the `native-tls` Cargo feature enabled.
237    #[cfg(feature = "native-tls")]
238    pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> {
239        Ok(Identity {
240            inner: ClientCert::Pkcs12(
241                native_tls_crate::Identity::from_pkcs12(der, password)
242                    .map_err(crate::error::builder)?,
243            ),
244        })
245    }
246
247    /// Parses a chain of PEM encoded X509 certificates, with the leaf certificate first.
248    /// `key` is a PEM encoded PKCS #8 formatted private key for the leaf certificate.
249    ///
250    /// The certificate chain should contain any intermediate cerficates that should be sent to
251    /// clients to allow them to build a chain to a trusted root.
252    ///
253    /// A certificate chain here means a series of PEM encoded certificates concatenated together.
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// # use std::fs;
259    /// # fn pkcs8() -> Result<(), Box<dyn std::error::Error>> {
260    /// let cert = fs::read("client.pem")?;
261    /// let key = fs::read("key.pem")?;
262    /// let pkcs8 = reqwest::Identity::from_pkcs8_pem(&cert, &key)?;
263    /// # drop(pkcs8);
264    /// # Ok(())
265    /// # }
266    /// ```
267    ///
268    /// # Optional
269    ///
270    /// This requires the `native-tls` Cargo feature enabled.
271    #[cfg(feature = "native-tls")]
272    pub fn from_pkcs8_pem(pem: &[u8], key: &[u8]) -> crate::Result<Identity> {
273        Ok(Identity {
274            inner: ClientCert::Pkcs8(
275                native_tls_crate::Identity::from_pkcs8(pem, key).map_err(crate::error::builder)?,
276            ),
277        })
278    }
279
280    /// Parses PEM encoded private key and certificate.
281    ///
282    /// The input should contain a PEM encoded private key
283    /// and at least one PEM encoded certificate.
284    ///
285    /// Note: The private key must be in RSA, SEC1 Elliptic Curve or PKCS#8 format.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// # use std::fs::File;
291    /// # use std::io::Read;
292    /// # fn pem() -> Result<(), Box<dyn std::error::Error>> {
293    /// let mut buf = Vec::new();
294    /// File::open("my-ident.pem")?
295    ///     .read_to_end(&mut buf)?;
296    /// let id = reqwest::Identity::from_pem(&buf)?;
297    /// # drop(id);
298    /// # Ok(())
299    /// # }
300    /// ```
301    ///
302    /// # Optional
303    ///
304    /// This requires the `rustls-tls(-...)` Cargo feature enabled.
305    #[cfg(feature = "__rustls")]
306    pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> {
307        use std::io::Cursor;
308
309        let (key, certs) = {
310            let mut pem = Cursor::new(buf);
311            let mut sk = Vec::<rustls::PrivateKey>::new();
312            let mut certs = Vec::<rustls::Certificate>::new();
313
314            for item in std::iter::from_fn(|| rustls_pemfile::read_one(&mut pem).transpose()) {
315                match item.map_err(|_| {
316                    crate::error::builder(TLSError::General(String::from(
317                        "Invalid identity PEM file",
318                    )))
319                })? {
320                    rustls_pemfile::Item::X509Certificate(cert) => {
321                        certs.push(rustls::Certificate(cert))
322                    }
323                    rustls_pemfile::Item::PKCS8Key(key) => sk.push(rustls::PrivateKey(key)),
324                    rustls_pemfile::Item::RSAKey(key) => sk.push(rustls::PrivateKey(key)),
325                    rustls_pemfile::Item::ECKey(key) => sk.push(rustls::PrivateKey(key)),
326                    _ => {
327                        return Err(crate::error::builder(TLSError::General(String::from(
328                            "No valid certificate was found",
329                        ))))
330                    }
331                }
332            }
333
334            if let (Some(sk), false) = (sk.pop(), certs.is_empty()) {
335                (sk, certs)
336            } else {
337                return Err(crate::error::builder(TLSError::General(String::from(
338                    "private key or certificate not found",
339                ))));
340            }
341        };
342
343        Ok(Identity {
344            inner: ClientCert::Pem { key, certs },
345        })
346    }
347
348    #[cfg(feature = "native-tls")]
349    pub(crate) fn add_to_native_tls(
350        self,
351        tls: &mut native_tls_crate::TlsConnectorBuilder,
352    ) -> crate::Result<()> {
353        match self.inner {
354            ClientCert::Pkcs12(id) | ClientCert::Pkcs8(id) => {
355                tls.identity(id);
356                Ok(())
357            }
358            #[cfg(feature = "__rustls")]
359            ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")),
360        }
361    }
362
363    #[cfg(feature = "__rustls")]
364    pub(crate) fn add_to_rustls(
365        self,
366        config_builder: rustls::ConfigBuilder<
367            rustls::ClientConfig,
368            rustls::client::WantsTransparencyPolicyOrClientCert,
369        >,
370    ) -> crate::Result<rustls::ClientConfig> {
371        match self.inner {
372            ClientCert::Pem { key, certs } => config_builder
373                .with_client_auth_cert(certs, key)
374                .map_err(crate::error::builder),
375            #[cfg(feature = "native-tls")]
376            ClientCert::Pkcs12(..) | ClientCert::Pkcs8(..) => {
377                Err(crate::error::builder("incompatible TLS identity type"))
378            }
379        }
380    }
381}
382
383impl fmt::Debug for Certificate {
384    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
385        f.debug_struct("Certificate").finish()
386    }
387}
388
389impl fmt::Debug for Identity {
390    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
391        f.debug_struct("Identity").finish()
392    }
393}
394
395/// A TLS protocol version.
396#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
397pub struct Version(InnerVersion);
398
399#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
400#[non_exhaustive]
401enum InnerVersion {
402    Tls1_0,
403    Tls1_1,
404    Tls1_2,
405    Tls1_3,
406}
407
408// These could perhaps be From/TryFrom implementations, but those would be
409// part of the public API so let's be careful
410impl Version {
411    /// Version 1.0 of the TLS protocol.
412    pub const TLS_1_0: Version = Version(InnerVersion::Tls1_0);
413    /// Version 1.1 of the TLS protocol.
414    pub const TLS_1_1: Version = Version(InnerVersion::Tls1_1);
415    /// Version 1.2 of the TLS protocol.
416    pub const TLS_1_2: Version = Version(InnerVersion::Tls1_2);
417    /// Version 1.3 of the TLS protocol.
418    pub const TLS_1_3: Version = Version(InnerVersion::Tls1_3);
419
420    #[cfg(feature = "default-tls")]
421    pub(crate) fn to_native_tls(self) -> Option<native_tls_crate::Protocol> {
422        match self.0 {
423            InnerVersion::Tls1_0 => Some(native_tls_crate::Protocol::Tlsv10),
424            InnerVersion::Tls1_1 => Some(native_tls_crate::Protocol::Tlsv11),
425            InnerVersion::Tls1_2 => Some(native_tls_crate::Protocol::Tlsv12),
426            InnerVersion::Tls1_3 => None,
427        }
428    }
429
430    #[cfg(feature = "__rustls")]
431    pub(crate) fn from_rustls(version: rustls::ProtocolVersion) -> Option<Self> {
432        match version {
433            rustls::ProtocolVersion::SSLv2 => None,
434            rustls::ProtocolVersion::SSLv3 => None,
435            rustls::ProtocolVersion::TLSv1_0 => Some(Self(InnerVersion::Tls1_0)),
436            rustls::ProtocolVersion::TLSv1_1 => Some(Self(InnerVersion::Tls1_1)),
437            rustls::ProtocolVersion::TLSv1_2 => Some(Self(InnerVersion::Tls1_2)),
438            rustls::ProtocolVersion::TLSv1_3 => Some(Self(InnerVersion::Tls1_3)),
439            _ => None,
440        }
441    }
442}
443
444pub(crate) enum TlsBackend {
445    // This is the default and HTTP/3 feature does not use it so suppress it.
446    #[allow(dead_code)]
447    #[cfg(feature = "default-tls")]
448    Default,
449    #[cfg(feature = "native-tls")]
450    BuiltNativeTls(native_tls_crate::TlsConnector),
451    #[cfg(feature = "__rustls")]
452    Rustls,
453    #[cfg(feature = "__rustls")]
454    BuiltRustls(rustls::ClientConfig),
455    #[cfg(any(feature = "native-tls", feature = "__rustls",))]
456    UnknownPreconfigured,
457}
458
459impl fmt::Debug for TlsBackend {
460    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
461        match self {
462            #[cfg(feature = "default-tls")]
463            TlsBackend::Default => write!(f, "Default"),
464            #[cfg(feature = "native-tls")]
465            TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls"),
466            #[cfg(feature = "__rustls")]
467            TlsBackend::Rustls => write!(f, "Rustls"),
468            #[cfg(feature = "__rustls")]
469            TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls"),
470            #[cfg(any(feature = "native-tls", feature = "__rustls",))]
471            TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"),
472        }
473    }
474}
475
476impl Default for TlsBackend {
477    fn default() -> TlsBackend {
478        #[cfg(all(feature = "default-tls", not(feature = "http3")))]
479        {
480            TlsBackend::Default
481        }
482
483        #[cfg(any(
484            all(feature = "__rustls", not(feature = "default-tls")),
485            feature = "http3"
486        ))]
487        {
488            TlsBackend::Rustls
489        }
490    }
491}
492
493#[cfg(feature = "__rustls")]
494pub(crate) struct NoVerifier;
495
496#[cfg(feature = "__rustls")]
497impl ServerCertVerifier for NoVerifier {
498    fn verify_server_cert(
499        &self,
500        _end_entity: &rustls::Certificate,
501        _intermediates: &[rustls::Certificate],
502        _server_name: &ServerName,
503        _scts: &mut dyn Iterator<Item = &[u8]>,
504        _ocsp_response: &[u8],
505        _now: std::time::SystemTime,
506    ) -> Result<ServerCertVerified, TLSError> {
507        Ok(ServerCertVerified::assertion())
508    }
509
510    fn verify_tls12_signature(
511        &self,
512        _message: &[u8],
513        _cert: &rustls::Certificate,
514        _dss: &DigitallySignedStruct,
515    ) -> Result<HandshakeSignatureValid, TLSError> {
516        Ok(HandshakeSignatureValid::assertion())
517    }
518
519    fn verify_tls13_signature(
520        &self,
521        _message: &[u8],
522        _cert: &rustls::Certificate,
523        _dss: &DigitallySignedStruct,
524    ) -> Result<HandshakeSignatureValid, TLSError> {
525        Ok(HandshakeSignatureValid::assertion())
526    }
527}
528
529/// Hyper extension carrying extra TLS layer information.
530/// Made available to clients on responses when `tls_info` is set.
531#[derive(Clone)]
532pub struct TlsInfo {
533    pub(crate) peer_certificate: Option<Vec<u8>>,
534}
535
536impl TlsInfo {
537    /// Get the DER encoded leaf certificate of the peer.
538    pub fn peer_certificate(&self) -> Option<&[u8]> {
539        self.peer_certificate.as_ref().map(|der| &der[..])
540    }
541}
542
543impl std::fmt::Debug for TlsInfo {
544    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
545        f.debug_struct("TlsInfo").finish()
546    }
547}
548
549#[cfg(test)]
550mod tests {
551    use super::*;
552
553    #[cfg(feature = "default-tls")]
554    #[test]
555    fn certificate_from_der_invalid() {
556        Certificate::from_der(b"not der").unwrap_err();
557    }
558
559    #[cfg(feature = "default-tls")]
560    #[test]
561    fn certificate_from_pem_invalid() {
562        Certificate::from_pem(b"not pem").unwrap_err();
563    }
564
565    #[cfg(feature = "native-tls")]
566    #[test]
567    fn identity_from_pkcs12_der_invalid() {
568        Identity::from_pkcs12_der(b"not der", "nope").unwrap_err();
569    }
570
571    #[cfg(feature = "native-tls")]
572    #[test]
573    fn identity_from_pkcs8_pem_invalid() {
574        Identity::from_pkcs8_pem(b"not pem", b"not key").unwrap_err();
575    }
576
577    #[cfg(feature = "__rustls")]
578    #[test]
579    fn identity_from_pem_invalid() {
580        Identity::from_pem(b"not pem").unwrap_err();
581    }
582
583    #[cfg(feature = "__rustls")]
584    #[test]
585    fn identity_from_pem_pkcs1_key() {
586        let pem = b"-----BEGIN CERTIFICATE-----\n\
587            -----END CERTIFICATE-----\n\
588            -----BEGIN RSA PRIVATE KEY-----\n\
589            -----END RSA PRIVATE KEY-----\n";
590
591        Identity::from_pem(pem).unwrap();
592    }
593
594    #[test]
595    fn certificates_from_pem_bundle() {
596        const PEM_BUNDLE: &[u8] = b"
597            -----BEGIN CERTIFICATE-----
598            MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5
599            MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
600            Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
601            A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
602            Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl
603            ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j
604            QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr
605            ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr
606            BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM
607            YyRIHN8wfdVoOw==
608            -----END CERTIFICATE-----
609
610            -----BEGIN CERTIFICATE-----
611            MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5
612            MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
613            Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
614            A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
615            Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi
616            9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk
617            M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB
618            /zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB
619            MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw
620            CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
621            1KyLa2tJElMzrdfkviT8tQp21KW8EA==
622            -----END CERTIFICATE-----
623        ";
624
625        assert!(Certificate::from_pem_bundle(PEM_BUNDLE).is_ok())
626    }
627}