Files
noq/quinn/certificate.html
T
2021-03-15 10:12:45 +00:00

360 lines
20 KiB
HTML

<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Certificate Configuration - Quinn</title>
<!-- Custom HTML head -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded "><a href="../networking-introduction.html"><strong aria-hidden="true">1.</strong> Networking introduction</a></li><li class="chapter-item expanded "><a href="../quic.html"><strong aria-hidden="true">2.</strong> QUIC introduction</a></li><li class="chapter-item expanded "><a href="../quinn.html"><strong aria-hidden="true">3.</strong> QUINN Introduction</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../quinn/certificate.html" class="active"><strong aria-hidden="true">3.1.</strong> Certificate Configuration</a></li><li class="chapter-item expanded "><a href="../quinn/set-up-connection.html"><strong aria-hidden="true">3.2.</strong> Connection Setup</a></li><li class="chapter-item expanded "><a href="../quinn/data-transfer.html"><strong aria-hidden="true">3.3.</strong> Data Transfer</a></li></ol></li><li class="chapter-item expanded "><a href="../quic.html"><strong aria-hidden="true">4.</strong> The QUIC protocol</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Quinn</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="certificates"><a class="header" href="#certificates">Certificates</a></h1>
<p>In this chapter, we discuss the configuration of the certificates that are <strong>required</strong> for a working Quinn connection. </p>
<p>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. </p>
<h2 id="insecure-connection"><a class="header" href="#insecure-connection">Insecure Connection</a></h2>
<p>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 <a href="https://github.com/ctz/rustls">rustls</a> <code>dangerous_configuration</code> feature flag is enabled, a client can be configured to trust any server.</p>
<p>Start by adding a <a href="https://github.com/ctz/rustls">rustls</a> dependency with the <code>dangerous_configuration</code> feature flag to your <code>Cargo.toml</code> file.</p>
<pre><code class="language-toml">quinn = &quot;*&quot;
rustls = { version = &quot;*&quot;, features = [&quot;dangerous_configuration&quot;, &quot;quic&quot;] }
</code></pre>
<p>Then, allow the client to skip the certificate validation by implementing <a href="https://docs.rs/rustls/latest/rustls/trait.ServerCertVerifier.html">ServerCertVerifier</a> and letting it assert verification for any server. </p>
<pre><pre class="playground"><code class="language-rust">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Implementation of `ServerCertVerifier` that verifies everything as trustworthy.
struct SkipCertificationVerification;
impl rustls::ServerCertVerifier for SkipCertificationVerification {
fn verify_server_cert(
&amp;self, _: &amp;rustls::RootCertStore, _: &amp;[rustls::Certificate], _: webpki::DNSNameRef, _: &amp;[u8],
) -&gt; Result&lt;rustls::ServerCertVerified, rustls::TLSError&gt; {
Ok(ServerCertVerified::assertion())
}
}
<span class="boring">}
</span></code></pre></pre>
<p>After that, modify the <a href="https://docs.rs/quinn/latest/quinn/generic/struct.ClientConfig.html">ClientConfig</a> to use this <a href="https://docs.rs/rustls/latest/rustls/trait.ServerCertVerifier.html">ServerCertVerifier</a> implementation. </p>
<pre><pre class="playground"><code class="language-rust">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn insecure() -&gt; ClientConfig {
let mut cfg = quinn::ClientConfigBuilder::default().build();
// Get a mutable reference to the 'crypto' config in the 'client config'.
let tls_cfg: &amp;mut rustls::ClientConfig =
std::sync::Arc::get_mut(&amp;mut cfg.crypto).unwrap();
// Change the certification verifier.
// This is only available when compiled with the 'dangerous_configuration' feature.
tls_cfg
.dangerous()
.set_certificate_verifier(Arc::new(SkipCertificationVerification));
cfg
}
<span class="boring">}
</span></code></pre></pre>
<p>Finally, if you plug this <a href="https://docs.rs/quinn/latest/quinn/generic/struct.ClientConfig.html">ClientConfig</a> into the <a href="https://docs.rs/quinn/latest/quinn/generic/struct.EndpointBuilder.html#method.default_client_config">EndpointBuilder::default_client_config()</a> your client endpoint should verify all connections as trustworthy.</p>
<h2 id="using-certificates"><a class="header" href="#using-certificates">Using Certificates</a></h2>
<p>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.</p>
<h3 id="self-signed-certificates"><a class="header" href="#self-signed-certificates">Self Signed Certificates</a></h3>
<p>Relying on <a href="https://en.wikipedia.org/wiki/Self-signed_certificate#:%7E:text=In%20cryptography%20and%20computer%20security,a%20CA%20aim%20to%20provide.">self-signed</a> 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 <a href="https://github.com/est31/rcgen">rcgen</a> crate or the openssl binary.
This example uses <a href="https://github.com/est31/rcgen">rcgen</a> to generate a certificate.</p>
<p>Let's look at an example:</p>
<pre><pre class="playground"><code class="language-rust">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn generate_self_signed_cert(cert_path: &amp;str, key_path: &amp;str) -&gt; anyhow::Result&lt;(quinn::Certificate, quinn::PrivateKey)&gt; {
// Generate dummy certificate.
let certificate = rcgen::generate_simple_self_signed(vec![&quot;localhost&quot;.into()]).unwrap();
let serialized_key = certificate.serialize_private_key_der();
let serialized_certificate = certificate.serialize_der().unwrap();
// Write to files.
fs::write(&amp;cert_path, &amp;serialized_certificate).context(&quot;failed to write certificate&quot;)?;
fs::write(&amp;key_path, &amp;serialized_key).context(&quot;failed to write private key&quot;)?;
let cert = quinn::Certificate::from_der(&amp;cert)?;
let key = quinn::PrivateKey::from_der(&amp;serialized_key)?;
Ok((cert, key))
}
<span class="boring">}
</span></code></pre></pre>
<p><em>Note that <a href="https://docs.rs/rcgen/latest/rcgen/fn.generate_simple_self_signed.html">generate_simple_self_signed</a> returns a <a href="https://en.wikipedia.org/wiki/Public_key_certificate">Certificate</a> that can be serialized to both <code>.der</code> and <code>.pem</code> formats.</em></p>
<h3 id="non-self-signed-certificates"><a class="header" href="#non-self-signed-certificates">Non-self-signed Certificates</a></h3>
<p>For this example, we use <a href="https://letsencrypt.org/getting-started/">Let's Encrypt</a>, a well-known Certificate Authority (<a href="https://en.wikipedia.org/wiki/Certificate_authority">CA</a>) (certificate issuer) which distributes certificates for free.</p>
<p><strong>Generate Certificate</strong></p>
<p><a href="https://certbot.eff.org/instructions">certbot</a> 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.</p>
<p>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.</p>
<p>If certbot is installed, run <code>certbot certonly --standalone</code>, this command will start a web server in the background and start the challenge.
certbot asks for the required data and writes the certificate to <code>cert.pem</code> and the private key to <code>privkey.pem</code>.<br />
These files can then be referenced in code.</p>
<pre><pre class="playground"><code class="language-rust">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn read_cert_from_file() -&gt; anyhow::Result&lt;(quinn::Certificate, quinn::PrivateKey)&gt; {
// Read from certificate and key from directory.
let (cert, key) = fs::read(&amp;&quot;./cert.pem&quot;).and_then(|x| Ok((x, fs::read(&amp;&quot;./privkey.pem&quot;)?)))?;
// Parse to certificate chain whereafter taking the first certifcater in this chain.
let cert = quinn::CertificateChain::from_pem(&amp;cert)?.iter().next().unwrap().clone();
let key = quinn::PrivateKey::from_pem(&amp;key)?;
Ok((quinn::Certificate::from(cert), key))
}
<span class="boring">}
</span></code></pre></pre>
<h3 id="configuring-certificates"><a class="header" href="#configuring-certificates">Configuring Certificates</a></h3>
<p>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 <code>Endpoint</code>.</p>
<p><strong>Configure Server</strong></p>
<pre><pre class="playground"><code class="language-rust">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let mut builder = ServerConfigBuilder::default();
builder.certificate(CertificateChain::from_certs(vec![certificate]), key)?;
<span class="boring">}
</span></code></pre></pre>
<p>This is the only thing you need to do for your server to be secured. </p>
<p><strong>Configure Client</strong></p>
<pre><pre class="playground"><code class="language-rust">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let mut builder = ClientConfigBuilder::default();
builder.add_certificate_authority(certificate)?;
<span class="boring">}
</span></code></pre></pre>
<p>This is the only thing you need to do for your client to trust a server certificate. </p>
<p><br><hr></p>
<p><a href="set-up-connection.html">Next</a>, let's have a look at how to set up a connection. </p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../quinn.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="../quinn/set-up-connection.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../quinn.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="../quinn/set-up-connection.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
</body>
</html>