当前位置:网站首页>tokio_ Rustls self signed certificate

tokio_ Rustls self signed certificate

2022-06-24 01:47:00 to listen attentively

1 Purpose of using self signed certificates

The purpose of using self signed certificates in this article :

  • Used by the server to verify whether the client is legal , Avoid connecting any client to the server .
  • be based on TLS, Encrypt the transmission data between the server and the client .

2 Self signed certificate verification process

How Client Certificate Authentication Works

3 principle

3.1 digital certificate

The server uses its own domain name to CA(Certificate Authority, Certification authority ) Apply for a certificate .

CA The certificate issued contains the public key 、 Certificate owner 、 The period of validity 、CA Use your private key to generate signatures and other information .

If the client wants to verify whether the certificate of the server is legal , Can see if it works CA Public key to unlock the signature on the certificate , If you can untie , The certificate of the server is legal . But how to confirm CA Your public key is legal , In case someone pretends to be CA What do I do , At this time, you can go through CA Of CA To verify , Go all the way up , Until it goes back to the famous root CA.

3.2 TLS

TLS(Transport Layer Security, Secure transport layer ),TLS yes Built on the transport layer TCP The agreement over the agreement , Serving the application layer , Its predecessor is SSL(Secure Socket Layer, Secure socket layer ), It realizes the encryption of the message in the application layer and then handed over to TCP Transmission function .

TLS The transmission process is roughly divided into two stages :

  • The first stage : The client and server use asymmetric encryption to exchange information , Used to generate the required for symmetric encrypted transmission key.
    • In the process , Use the private key to encrypt the data , Decrypt the data using the public key in the opposite certificate .
  • The second stage : Use generated key Encrypt and decrypt the communication data .

4 Self signed certificate generation

Reference resources rustls The example given has been modified .

  • Do it yourself CA, Generate CA cert, In the future CA cert It can be trusted by the server and the client .
  • utilize CA cert Issue the server and client SSL certificate . Server and client SSL The certificate generation steps are the same , As follows :
    • Generate private key .
    • Using private key to generate certificate request .
    • Utilize certificate requests and CA cert Generate Certificate ,CA Will use its own private key to generate a certificate signature .

build-a-pki.sh:

#!/bin/sh

set -xe

work_dir=$(cd $(dirname $0); pwd)
dir='dev'

if [ $# -eq 1 ]; then
  while getopts ":r" opt
  do
    case $opt in
        r)
        dir='release'
        ;;
        ?)
        echo "Unknow input"
        exit 1
        ;;
    esac
  done
fi

dir=$work_dir/$dir

rm -rf $dir
mkdir $dir

openssl req -nodes \
          -x509 \
          -days 3650 \
          -newkey rsa:4096 \
          -keyout $dir/ca.key \
          -out $dir/ca.cert \
          -sha256 \
          -batch \
          -subj "/CN=ponytown RSA CA"

openssl req -nodes \
          -newkey rsa:2048 \
          -keyout $dir/server.key \
          -out $dir/server.req \
          -sha256 \
          -batch \
          -subj "/CN=testserver.com"

openssl rsa \
          -in $dir/server.key \
          -out $dir/server.rsa

openssl req -nodes \
          -newkey rsa:2048 \
          -keyout $dir/client.key \
          -out $dir/client.req \
          -sha256 \
          -batch \
          -subj "/CN=ponytown client"

for kt in $dir ; do
  openssl x509 -req \
            -in $kt/server.req \
            -out $kt/server.cert \
            -CA $kt/ca.cert \
            -CAkey $kt/ca.key \
            -sha256 \
            -days 2000 \
            -set_serial 456 \
            -extensions v3_server -extfile openssl.cnf

  openssl x509 -req \
            -in $kt/client.req \
            -out $kt/client.cert \
            -CA $kt/ca.cert \
            -CAkey $kt/ca.key \
            -sha256 \
            -days 2000 \
            -set_serial 789 \
            -extensions v3_client -extfile openssl.cnf

  cat $kt/server.cert $kt/ca.cert > $kt/server.fullchain
done

rm $dir/*.req
rm $dir/ca.key
rm $dir/server.cert $dir/server.key

openssl.cnf:

[ v3_server ]
basicConstraints = critical,CA:false
keyUsage = nonRepudiation, digitalSignature
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
subjectAltName = @alt_names

[ v3_client ]
basicConstraints = critical,CA:false
keyUsage = nonRepudiation, digitalSignature
extendedKeyUsage = critical, clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always

[ alt_names ]
DNS.1 = testserver.com
DNS.2 = second.testserver.com
DNS.3 = localhost

4 Use the self signed certificate example

This article is based on rust in tokio_rustls Library implementation TLS.TLS yes Based on the TCP Above , Serving the application layer , It encrypts the message of the application layer and then submits it to TCP transmitted .

client :

  • Certificate of loading , Generate rustls::ClientConfig
  • utilize rustls::ClientConfig Generate rustls::TlsConnector
  • utilize rustls::TlsConnector take tokio::net::TcpStream To tokio_rustls::client::TlsStream, Available later TlsStream Sending and receiving data

Server side :

  • Certificate of loading , Generate rustls::ServerConfig
  • utilize rustls::ServerConfig Generate rustls::TlsAcceptor
  • utilize rustls::TlsAcceptor take tokio::net::TcpStream To tokio_rustls::server::TlsStream, Available later TlsStream Sending and receiving data
use std::convert::TryFrom;
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::sync::Arc;
use std::net::{
    SocketAddr,
    ToSocketAddrs,
};
use rustls::{
    RootCertStore,
    server::{AllowAnyAuthenticatedClient},
};
use tokio::net::{TcpStream};
use tokio_rustls::{
    TlsAcceptor,
    TlsConnector,
    rustls::{self},
    client::TlsStream as ClientTlsStream,
};

pub fn load_certs(filename: &str) -> Vec<rustls::Certificate> {
    let certfile = File::open(filename).expect("cannot open certificate file");
    let mut reader = BufReader::new(certfile);
    rustls_pemfile::certs(&mut reader)
        .unwrap()
        .iter()
        .map(|v| rustls::Certificate(v.clone()))
        .collect()
}

pub fn load_private_key(filename: &str) -> rustls::PrivateKey {
    let keyfile = File::open(filename).expect("cannot open private key file");
    let mut reader = BufReader::new(keyfile);

    loop {
        match rustls_pemfile::read_one(&mut reader).expect("cannot parse private key .pem file") {
            Some(rustls_pemfile::Item::RSAKey(key)) => return rustls::PrivateKey(key),
            Some(rustls_pemfile::Item::PKCS8Key(key)) => return rustls::PrivateKey(key),
            None => break,
            _ => {}
        }
    }

    panic!(
        "no keys found in {:?} (encrypted keys not supported)",
        filename
    );
}

pub fn lookup_ipv4(host: &str, port: u16) -> SocketAddr {
    let addrs = (host, port).to_socket_addrs().unwrap();
    for addr in addrs {
        if let SocketAddr::V4(_) = addr {
            return addr;
        }
    }

    unreachable!("Cannot lookup address");
}

fn make_client_config(ca_file: &str, certs_file: &str, key_file: &str) -> Arc<rustls::ClientConfig> {
    let cert_file = File::open(&ca_file).expect("Cannot open CA file");
    let mut reader = BufReader::new(cert_file);

    let mut root_store = RootCertStore::empty();
    root_store.add_parsable_certificates(&rustls_pemfile::certs(&mut reader).unwrap());

    let suites = rustls::DEFAULT_CIPHER_SUITES.to_vec();
    let versions = rustls::DEFAULT_VERSIONS.to_vec();

    let certs = load_certs(certs_file);
    let key = load_private_key(key_file);

    let config = rustls::ClientConfig::builder()
        .with_cipher_suites(&suites)
        .with_safe_default_kx_groups()
        .with_protocol_versions(&versions)
        .expect("inconsistent cipher-suite/versions selected")
        .with_root_certificates(root_store)
        .with_single_cert(certs, key)
        .expect("invalid client auth certs/key");
    Arc::new(config)
}

fn make_server_config(certs: &str, key_file: &str) -> Arc<rustls::ServerConfig> {
    let roots = load_certs(certs);
    let certs = roots.clone();
    let mut client_auth_roots = RootCertStore::empty();
    for root in roots {
        client_auth_roots.add(&root).unwrap();
    }
    let client_auth = AllowAnyAuthenticatedClient::new(client_auth_roots);

    let privkey = load_private_key(key_file);
    let suites = rustls::ALL_CIPHER_SUITES.to_vec();
    let versions = rustls::ALL_VERSIONS.to_vec();

    let mut config = rustls::ServerConfig::builder()
        .with_cipher_suites(&suites)
        .with_safe_default_kx_groups()
        .with_protocol_versions(&versions)
        .expect("inconsistent cipher-suites/versions specified")
        .with_client_cert_verifier(client_auth)
        .with_single_cert_with_ocsp_and_sct(certs, privkey, vec![], vec![])
        .expect("bad certificates/private key");

    config.key_log = Arc::new(rustls::KeyLogFile::new());
    config.session_storage = rustls::server::ServerSessionMemoryCache::new(256);
    Arc::new(config)
}

pub async fn new_tls_stream(domain: &str, addr: std::net::SocketAddr, 
                            ca_file: &str, cert_file: &str, key_file: &str) -> ClientTlsStream<TcpStream> {
    let config = make_client_config(&ca_file, &cert_file, &key_file);

    let connector = TlsConnector::from(config);

    let stream = TcpStream::connect(&addr).await.unwrap();
    let domain = rustls::ServerName::try_from(domain)
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid dnsname")).unwrap();
    let stream = connector.connect(domain, stream).await.unwrap();
    stream
}

pub fn new_tls_acceptor(cert_file: &str, key_file: &str) -> TlsAcceptor {
    let config = make_server_config(&cert_file, &key_file);
    let acceptor = TlsAcceptor::from(config);
    acceptor
}

#[cfg(test)]
mod tests {
    use super::*;
    use tokio::net::TcpListener;
    use tokio::io::{AsyncWriteExt, AsyncReadExt};

    const CA_FILE: &str = "cert/dev/ca.cert";
    const CLIENT_CERT_FILE: &str = "cert/dev/client.cert";
    const CLIENT_KEY_FILE: &str = "cert/dev/client.key";
    const SERVER_CERT_FILE: &str = "cert/dev/server.fullchain";
    const SERVER_KEY_FILE: &str = "cert/dev/server.rsa";

    #[tokio::test]
    async fn tls() {
        let msg = b"Hello world\n";
        let mut buf = [0; 12];

        start_server().await;

        start_client(msg, &mut buf).await;
        assert_eq!(&buf, msg);
    }

    async fn start_server() {
        let tls_acceptor = new_tls_acceptor(SERVER_CERT_FILE, SERVER_KEY_FILE);
        let listener = TcpListener::bind("0.0.0.0:5002").await.unwrap();

        tokio::spawn(async move {
            let (stream, _peer_addr) = listener.accept().await.unwrap();
            let mut tls_stream = tls_acceptor.accept(stream).await.unwrap();
            println!("server: Accepted client conn with TLS");

            let mut buf = [0; 12];
            tls_stream.read(&mut buf).await.unwrap();
            println!("server: got data: {:?}", buf);
            tls_stream.write(&buf).await.unwrap();
            println!("server: flush the data out");
        });
    }

    async fn start_client(msg: &[u8], buf: &mut [u8]) {
        let addr = lookup_ipv4("127.0.0.1", 5002);
        let mut tls_stream =
            new_tls_stream("localhost", addr, CA_FILE, CLIENT_CERT_FILE, CLIENT_KEY_FILE).await;

        tls_stream.write(msg).await.unwrap();
        println!("client: send data");

        tls_stream.read(buf).await.unwrap();
        println!("client: read echoed data");
    }
}

Reference resources

Liu chao . HTTPS agreement : The process of ordering takeout turns out to be so complicated .

https://www.jscape.com/blog/client-certificate-authentication

https://www.makethenmakeinstall.com/2014/05/ssl-client-authentication-step-by-step/

https://www.jianshu.com/p/1fc7130eb2c2

原网站

版权声明
本文为[to listen attentively]所创,转载请带上原文链接,感谢
https://yzsam.com/2021/11/20211113153825261E.html