Certificates
In this chapter, we discuss the configuration of the certificates that are required for a working Quinn connection.
As QUIC uses TLS 1.3 for authentication of connections, the server needs to provide the client with a certificate confirming its identity, and the client must be configured to trust the certificates it receives from the server.
Insecure Connection
For our example use case, the easiest way to allow the client to trust our server is to disable certificate verification (don't do this in production!).
When the rustls dangerous_configuration
feature flag is enabled, a client can be configured to trust any server.
Start by adding a rustls dependency with the dangerous_configuration
feature flag to your Cargo.toml
file.
quinn = "0.11"
rustls = "0.23"
Then, allow the client to skip the certificate validation by implementing ServerCertVerifier and letting it assert verification for any server.
#![allow(unused)] fn main() { // Implementation of `ServerCertVerifier` that verifies everything as trustworthy. #[derive(Debug)] struct SkipServerVerification(Arc<CryptoProvider>); impl SkipServerVerification { fn new() -> Arc<Self> { Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) } } impl danger::ServerCertVerifier for SkipServerVerification { fn verify_server_cert( &self, _end_entity: &CertificateDer<'_>, _intermediates: &[CertificateDer<'_>], _server_name: &ServerName<'_>, _ocsp: &[u8], _now: UnixTime, ) -> Result<danger::ServerCertVerified, rustls::Error> { Ok(danger::ServerCertVerified::assertion()) } fn verify_tls12_signature( &self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct, ) -> Result<danger::HandshakeSignatureValid, rustls::Error> { verify_tls12_signature( message, cert, dss, &self.0.signature_verification_algorithms, ) } fn verify_tls13_signature( &self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct, ) -> Result<danger::HandshakeSignatureValid, rustls::Error> { verify_tls13_signature( message, cert, dss, &self.0.signature_verification_algorithms, ) } fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { self.0.signature_verification_algorithms.supported_schemes() } } }
After that, modify the ClientConfig to use this ServerCertVerifier implementation.
#![allow(unused)] fn main() { fn configure_client() -> Result<ClientConfig, NoInitialCipherSuite> { let crypto = rustls::ClientConfig::builder() .dangerous() .with_custom_certificate_verifier(SkipServerVerification::new()) .with_no_client_auth(); Ok(ClientConfig::new(Arc::new(QuicClientConfig::try_from( crypto, )?))) } }
Finally, if you plug this ClientConfig into the Endpoint::set_default_client_config() your client endpoint should verify all connections as trustworthy.
Using Certificates
In this section, we look at certifying an endpoint with a certificate. The certificate can be signed with its key, or with a certificate authority's key.
Self Signed Certificates
Relying on self-signed certificates means that clients allow servers to sign their certificates. This is simpler because no third party is involved in signing the server's certificate. However, self-signed certificates do not protect users from person-in-the-middle attacks, because an interceptor can trivially replace the certificate with one that it has signed. Self-signed certificates, among other options, can be created using the rcgen crate or the openssl binary. This example uses rcgen to generate a certificate.
Let's look at an example:
#![allow(unused)] fn main() { fn generate_self_signed_cert() -> Result<(CertificateDer<'static>, PrivatePkcs8KeyDer<'static>), Box<dyn Error>> { let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()])?; let cert_der = CertificateDer::from(cert.cert); let key = PrivatePkcs8KeyDer::from(cert.key_pair.serialize_der()); Ok((cert_der, key)) } }
Note that generate_simple_self_signed returns a Certificate that can be serialized to both .der
and .pem
formats.
Non-self-signed Certificates
For this example, we use Let's Encrypt, a well-known Certificate Authority (CA) (certificate issuer) which distributes certificates for free.
Generate Certificate
certbot can be used with Let's Encrypt to generate certificates; its website comes with clear instructions. Because we're generating a certificate for an internal test server, the process used will be slightly different compared to what you would do when generating certificates for an existing (public) website.
On the certbot website, select that you do not have a public web server and follow the given installation instructions. certbot must answer a cryptographic challenge of the Let's Encrypt API to prove that you control the domain. It needs to listen on port 80 (HTTP) or 443 (HTTPS) to achieve this. Open the appropriate port in your firewall and router.
If certbot is installed, run certbot certonly --standalone
, this command will start a web server in the background and start the challenge.
certbot asks for the required data and writes the certificates to fullchain.pem
and the private key to privkey.pem
.
These files can then be referenced in code.
#![allow(unused)] fn main() { fn read_certs_from_file() -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>), Box<dyn Error>> { let certs = CertificateDer::pem_file_iter("./fullchain.pem") .unwrap() .map(|cert| cert.unwrap()) .collect(); let key = PrivateKeyDer::from_pem_file("./privkey.pem").unwrap(); Ok((certs, key)) } }
Configuring Certificates
Now that you have a valid certificate, the client and server need to be configured to use it.
After configuring plug the configuration into the Endpoint
.
Configure Server
#![allow(unused)] fn main() { let server_config = quinn::ServerConfig::with_single_cert(certs, key); }
This is the only thing you need to do for your server to be secured.
Configure Client
#![allow(unused)] fn main() { let client_config = quinn::ClientConfig::with_platform_verifier(); }
This is the only thing you need to do for your client to trust a server certificate signed by a conventional certificate authority.
Next, let's have a look at how to set up a connection.