最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

ssl - How to make a TLS connection between two peers using Rust tokio_native_tls - Stack Overflow

matteradmin5PV0评论

I am working in a file transfer system that has p2p connectivity library using Rust. It works using just tcp but now I want to improve it using TLS but I do not understand 2 things. First is how can 2 peers share the Certificate Authorities CA so they can verify that the connection is trustworthy.

Secondly, I am trying to use the tokio_native_tls which wraps the native_tls library and adds async. I am following the examples in the native_tls docs. However they use connector with a domain and as the domain they use Google.

This is the example they provide.

use native_tls::TlsConnector;
use std::io::{Read, Write};
use std::net::TcpStream;

let connector = TlsConnector::new().unwrap();

let stream = TcpStream::connect("google:443").unwrap();
let mut stream = connector.connect("google", stream).unwrap();

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

In the peer to peer connection I describe there is only 2 peers trying to transfer some files. Since they don't have a domain only IP it would not work. I am pretty lost. How can I approach this project?

I am working in a file transfer system that has p2p connectivity library using Rust. It works using just tcp but now I want to improve it using TLS but I do not understand 2 things. First is how can 2 peers share the Certificate Authorities CA so they can verify that the connection is trustworthy.

Secondly, I am trying to use the tokio_native_tls which wraps the native_tls library and adds async. I am following the examples in the native_tls docs. However they use connector with a domain and as the domain they use Google.

This is the example they provide.

use native_tls::TlsConnector;
use std::io::{Read, Write};
use std::net::TcpStream;

let connector = TlsConnector::new().unwrap();

let stream = TcpStream::connect("google:443").unwrap();
let mut stream = connector.connect("google", stream).unwrap();

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

In the peer to peer connection I describe there is only 2 peers trying to transfer some files. Since they don't have a domain only IP it would not work. I am pretty lost. How can I approach this project?

Share Improve this question edited Nov 23, 2024 at 19:41 halfer 20.4k19 gold badges109 silver badges202 bronze badges asked Nov 16, 2024 at 4:43 David Martínez GilDavid Martínez Gil 3431 gold badge2 silver badges7 bronze badges 1
  • The version of this question with code was much better, and this was the version that was answered. Since the later version without code was more vague, I restored the earlier version. In general, when a question has garnered an answer, it is best to only apply minor edits, so as to not invalidate any part of an answer. – halfer Commented Nov 23, 2024 at 19:42
Add a comment  | 

1 Answer 1

Reset to default 3

native-tls

The docs for the synchronous connect function say that:

The domain is ignored if both SNI and hostname verification are disabled.

Taking that into account, switching to the async API's, and generating a self-signed certificate, we arrive at the following working code:

[package]
name = "tls"
version = "0.1.0"
edition = "2021"

[dependencies]
rcgen = "0.13.1"
tokio = { version = "1.41.1", features = ["full"] }
tokio-native-tls = "0.3.1"
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio_native_tls::native_tls::Identity;
use tokio_native_tls::{native_tls, TlsAcceptor, TlsConnector};

#[tokio::main]
async fn main() {
    let cert = rcgen::generate_simple_self_signed([]).unwrap();
    let listener = TcpListener::bind("127.0.0.1:8001").await.unwrap();

    let trusted = native_tls::Certificate::from_pem(cert.cert.pem().as_bytes()).unwrap();
    tokio::spawn(async move {
        let connector = TlsConnector::from(
            native_tls::TlsConnector::builder()
                .disable_built_in_roots(true)
                .add_root_certificate(trusted)
                .danger_accept_invalid_hostnames(true)
                .use_sni(false)
                .build()
                .unwrap(),
        );
        let tcp = TcpStream::connect("0.0.0.0:8001").await.unwrap();
        let mut tls = connector.connect("N/A", tcp).await.unwrap();
        tls.write_all(b"hello!").await.unwrap();
        tls.shutdown().await.unwrap();
    });

    let acceptor = TlsAcceptor::from(
        native_tls::TlsAcceptor::new(
            Identity::from_pkcs8(
                cert.cert.pem().as_bytes(),
                cert.key_pair.serialize_pem().as_bytes(),
            )
            .unwrap(),
        )
        .unwrap(),
    );

    let (tcp, _) = listener.accept().await.unwrap();
    let mut tls = acceptor.accept(tcp).await.unwrap();
    let mut buf = String::new();
    tls.read_to_string(&mut buf).await.unwrap();
    println!("read: {buf}");
}

rustls

Here is similar code using rustls. Differences include:

  • You can and should pass the server IP instead of "N/A" for server name.
  • The default certificate verifier requires the IP to be among the subject names.
[package]
name = "tls"
version = "0.1.0"
edition = "2021"

[dependencies]
rcgen = "0.13.1"
tokio = { version = "1.41.1", features = ["full"] }
tokio-rustls = "0.26.0"
use std::net::Ipv4Addr;
use std::sync::Arc;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio_rustls::rustls::pki_types::pem::PemObject;
use tokio_rustls::rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer, ServerName};
use tokio_rustls::rustls::{ClientConfig, RootCertStore};
use tokio_rustls::{rustls, TlsAcceptor, TlsConnector};

#[tokio::main]
async fn main() {
    let cert = rcgen::generate_simple_self_signed(["127.0.0.1".to_owned()]).unwrap();
    let listener = TcpListener::bind("127.0.0.1:8001").await.unwrap();

    let mut trusted = RootCertStore::empty();
    trusted.add(cert.cert.der().clone()).unwrap();
    tokio::spawn(async move {
        let connector: TlsConnector = TlsConnector::from(Arc::new(
            ClientConfig::builder()
                .with_root_certificates(trusted)
                .with_no_client_auth(),
        ));
        let tcp = TcpStream::connect("127.0.0.1:8001").await.unwrap();
        let mut tls = connector
            .connect(
                ServerName::IpAddress(Ipv4Addr::new(127, 0, 0, 1).into()),
                tcp,
            )
            .await
            .unwrap();
        tls.write_all(b"hello!").await.unwrap();
        tls.shutdown().await.unwrap();
    });

    let acceptor = TlsAcceptor::from(Arc::new(
        rustls::ServerConfig::builder()
            .with_no_client_auth()
            .with_single_cert(
                vec![cert.cert.der().clone()],
                PrivateKeyDer::Pkcs8(
                    PrivatePkcs8KeyDer::from_pem_slice(cert.key_pair.serialize_pem().as_bytes())
                        .unwrap(),
                ),
            )
            .unwrap(),
    ));

    let (tcp, _) = listener.accept().await.unwrap();
    let mut tls = acceptor.accept(tcp).await.unwrap();
    let mut buf = String::new();
    tls.read_to_string(&mut buf).await.unwrap();
    println!("read: {buf}");
}

If you wanted to use a CA-signed certificate, you would add the CA certificate as a trusted root instead.

Post a comment

comment list (0)

  1. No comments so far