Sling Academy
Home/Rust/Implementing TLS/SSL in Rust with native-tls or rustls

Implementing TLS/SSL in Rust with native-tls or rustls

Last updated: January 06, 2025

Transport Layer Security (TLS) and Secure Sockets Layer (SSL) are cryptographic protocols that provide security over a computer network. Rust, with its focus on safety and performance, provides powerful libraries for implementing TLS/SSL: native-tls and rustls. This guide will cover how to use both libraries to secure network communications in your Rust applications.

Using native-tls

The native-tls library provides a platform-independent TLS implementation that relies on the system’s native TLS library. This means it uses the underlying SSL libraries available on your operating system (such as SChannel on Windows, Secure Transport on macOS, or OpenSSL on Linux). Here’s how you can use native-tls to establish a secure connection:

Installing native-tls

[dependencies]
native-tls = "0.2"

Creating a TLS Connector

To create a simple TLS client, you can use the following Rust code:

use native_tls::TlsConnector;
use std::net::TcpStream;

fn secure_connection() -> Result<(), Box> {
    let connector = TlsConnector::new()?;
    let stream = TcpStream::connect("www.rust-lang.org:443")?;
    let mut stream = connector.connect("www.rust-lang.org", stream)?;

    stream.write_all(b"GET / HTTP/1.0\r\n\r\n")?;
    let mut res = vec![];
    stream.read_to_end(&mut res)?;
    println!("Response: {}", String::from_utf8_lossy(&res));
    Ok(())
}

In the above example, we create a TlsConnector and establish a connection to a server. The connect method upgrades the TcpStream to a secured connection.

Using rustls

rustls is a modern TLS library written in Rust. It does not depend on the native libraries of the host system, making it a portable and consistent choice across platforms. Unlike native-tls, rustls is focused on implementing TLS in a way that is safe from known and future such as time-related vulnerabilities due to its careful design and avoidance of possible misuse of the APIs. Here's how to implement it:

Installing rustls

[dependencies]
rustls = "0.20"
tokio = { version = "1", features = ["full"] }
webpki-roots = "0.21"

Creating a TLS Connection with rustls

Below is a basic example of creating a client with rustls:

use rustls::{ClientConfig, ClientSession};
use rustls::Stream;
use std::sync::Arc;
use tokio::net::TcpStream;
use webpki::DNSNameRef;

async fn secure_connection() -> Result<(), Box> {
    let config = Arc::new(rustls::ClientConfig::new());
    let dns_name = DNSNameRef::try_from_ascii_str("www.googleapis.com")?;
    let mut session = ClientSession::new(&config, dns_name);
    let stream = TcpStream::connect("216.58.207.36:443").await?;
    let mut tls_stream = Stream::new(&mut session, stream);

    tls_stream.write_all("GET / HTTP/1.0\r\n\r\n").await?;
    let mut res = Vec::new();
    tls_stream.read_to_end(&mut res).await?;
    println!("Response: {}", String::from_utf8_lossy(&res));

    Ok(())
}

In this example, a TLS connection is established using rustls. The Rust program leverages the tokio library for asynchronous I/O, making it suitable for high-performance applications requiring concurrency.

Choosing Between native-tls and rustls

The choice between native-tls and rustls often depends on the specific requirements of your project. native-tls might be preferred when compatibility with native systems is necessary, whereas rustls is ideal for deploying portable software with considerations for explicit security and portability.

Both libraries are excellent for securing your Rust applications, and it's crucial to thoroughly test whether your application suits them, considering aspects like performance, security, and cross-platform distribution. Furthermore, continuous updates and maintenance are critical to mitigating security vulnerabilities in any TLS/SSL implementation.

Next Article: Building a Simple TCP Echo Server in Rust

Previous Article: Sending and Receiving Emails in Rust Using lettre

Series: Networking in Rust

Rust

You May Also Like

  • E0557 in Rust: Feature Has Been Removed or Is Unavailable in the Stable Channel
  • Network Protocol Handling Concurrency in Rust with async/await
  • Using the anyhow and thiserror Crates for Better Rust Error Tests
  • Rust - Investigating partial moves when pattern matching on vector or HashMap elements
  • Rust - Handling nested or hierarchical HashMaps for complex data relationships
  • Rust - Combining multiple HashMaps by merging keys and values
  • Composing Functionality in Rust Through Multiple Trait Bounds
  • E0437 in Rust: Unexpected `#` in macro invocation or attribute
  • Integrating I/O and Networking in Rust’s Async Concurrency
  • E0178 in Rust: Conflicting implementations of the same trait for a type
  • Utilizing a Reactor Pattern in Rust for Event-Driven Architectures
  • Parallelizing CPU-Intensive Work with Rust’s rayon Crate
  • Managing WebSocket Connections in Rust for Real-Time Apps
  • Downloading Files in Rust via HTTP for CLI Tools
  • Mocking Network Calls in Rust Tests with the surf or reqwest Crates
  • Rust - Designing advanced concurrency abstractions using generic channels or locks
  • Managing code expansion in debug builds with heavy usage of generics in Rust
  • Implementing parse-from-string logic for generic numeric types in Rust
  • Rust.- Refining trait bounds at implementation time for more specialized behavior