在邮件服务器上将 TcpStream 升级到 TLS

问题描述 投票:0回答:1

我正在使用 Rust 从头开始构建自己的邮件服务器。我使用 tokio::new::TcpListener 设置了简单的 TCP 服务器,并使用 tokio::spawn 来处理每个连接。我用 BufReader 包装了流。然后我向客户端发送 220 响应。所以现在在客户端发送 EHLO 然后 STARTTLS 之后,我想升级流以使用 TLS。我知道我可以实现自己的 TLS,但这不是最好的主意。所以我的问题是:在命令之后如何升级到 TLS。

这是我生成证书的代码:

rm *.pem

openssl req -x509 -newkey rsa:4096 -days 3650 -keyout ca-key.pem -out ca-cert.pem -subj "..."

echo "CA's self-signed certificate"
openssl x509 -in ca-cert.pem -noout -text

openssl req -newkey rsa:4096 -keyout server-key.pem -out server-req.pem -subj "..."
openssl x509 -in ca-cert.pem -noout -text

openssl x509 -req -in server-req.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile server-ext.cnf

echo "Servers's self-signed certificate"
openssl x509 -in server-cert.pem -noout -text

openssl pkcs12 -export -out cert.pfx -inkey server-key.pem -in server-cert.pem -certfile ca-cert.pem

这是我对

native_tls
的基本实现,但是
accept
功能不起作用。

// server.rs
loop {
    match server.accept().await {
        Ok((stream, address)) => {
            tokio::spawn(async {
                Connection::handle(stream).await;
            });
        }
        Err(e) => {
            error!("{e}");
        }
    }
}
// connection.rs fn handle
let mut buffer = String::new();

self.write("220 swiftmail.app ESMTP SwiftMail\r\n").await;

loop {
    match self.stream.read_line(&mut buffer).await {
        Ok(0) => break,
        Ok(_bytes) => {
            let parts = buffer.split_ascii_whitespace().collect::<Vec<&str>>();
            let command = buffer.clone();
            // STARTTLS / EHLO
            let command_name = parts.get(0).unwrap();

            match command_name {
                "EHLO" => self.ehlo(command).await,
                "STARTTLS" => self.starttls(command).await,
                _ => {}
            };
        },
        _ => {}
    }

    buffer.clear();
}
// starttls.rs
conn.write("220 GO HEAD\r\n").await;

let stream = conn.stream.get_mut(); // &mut TcpStream

let mut file = File::open("C:/Dev/mail-server/src/cert/cert.pfx").unwrap();
let mut identity = vec![];
file.read_to_end(&mut identity).unwrap();

let identity = Identity::from_pkcs12(&identity, "password").unwrap();

let acceptor = TlsAcceptor::new(identity).unwrap();
let acceptor = tokio_native_tls::TlsAcceptor::from(acceptor);
email ssl rust starttls
1个回答
0
投票

要在收到

STARTTLS
命令后将 Rust 中的 TCP 连接升级到 TLS,您可以使用
tokio_native_tls
crate,它提供了与 Tokio 一起使用的异步 TLS/SSL 流。您已经掌握了问题的基础知识,但让我们对其进行改进并将其适应您的应用程序的上下文。

使用

tokio-native-tls
并将
native-tls
添加到您的
Cargo.toml

您应该重构您的

starttls
函数来升级流:

use tokio::net::TcpStream;
use tokio_native_tls::TlsAcceptor;
use native_tls::Identity;
use std::fs::File;
use std::io::Read;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

async fn upgrade_to_tls(tcp_stream: TcpStream) -> tokio::io::Result<()> {
    let mut file = File::open("path_to_cert/cert.pfx").unwrap();
    let mut identity_data = vec![];
    file.read_to_end(&mut identity_data).unwrap();

    let identity = Identity::from_pkcs12(&identity_data, "password").unwrap();
    let tls_acceptor = native_tls::TlsAcceptor::new(identity).unwrap();
    let acceptor = TlsAcceptor::from(tls_acceptor);

    let mut tls_stream = acceptor.accept(tcp_stream).await.unwrap();

    // You can now read and write to `tls_stream` as your TLS-secured connection
    tls_stream.write_all("220 Ready for secure communication\r\n".as_bytes()).await?;

    // Example of reading from the stream
    let mut buffer = vec![0; 1024];
    let n = tls_stream.read(&mut buffer).await?;
    println!("Received: {}", String::from_utf8_lossy(&buffer[..n]));

    Ok(())
}

这意味着您必须先准备 TLS Identity,例如从包含证书和私钥的 PKCS12 (

.pfx
) 文件创建一个
native_tls::Identity
实例。 TLS 接受方使用此身份来执行 TLS 握手。

在主连接处理循环中,收到

STARTTLS
命令后,您将调用此
upgrade_to_tls
函数,并将 TCP 流 (
TcpStream
) 作为参数传递。

© www.soinside.com 2019 - 2024. All rights reserved.