Rust-Rust native tls: rust-native-tls — Bindings for native TLS libraries

rust-native-tls

CircleCI Build Status

Documentation

An abstraction over platform-specific TLS implementations.

Specifically, this crate uses SChannel on Windows (via the schannel crate), Secure Transport on macOS (via the security-framework crate), and OpenSSL (via the openssl crate) on all other platforms.

Installation

# Cargo.toml
[dependencies]
native-tls = "0.2"

Usage

An example client looks like:

extern crate native_tls;

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

fn main() {
    let connector = TlsConnector::new().unwrap();

    let stream = TcpStream::connect("google.com:443").unwrap();
    let mut stream = connector.connect("google.com", 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));
}

To accept connections as a server from remote clients:

extern crate native_tls;

use native_tls::{Identity, TlsAcceptor, TlsStream};
use std::fs::File;
use std::io::{Read};
use std::net::{TcpListener, TcpStream};
use std::sync::Arc;
use std::thread;

fn main() {
    let mut file = File::open("identity.pfx").unwrap();
    let mut identity = vec![];
    file.read_to_end(&mut identity).unwrap();
    let identity = Identity::from_pkcs12(&identity, "hunter2").unwrap();

    let acceptor = TlsAcceptor::new(identity).unwrap();
    let acceptor = Arc::new(acceptor);

    let listener = TcpListener::bind("0.0.0.0:8443").unwrap();

    fn handle_client(stream: TlsStream<TcpStream>) {
        // ...
    }

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                let acceptor = acceptor.clone();
                thread::spawn(move || {
                    let stream = acceptor.accept(stream).unwrap();
                    handle_client(stream);
                });
            }
            Err(e) => { /* connection failed */ }
        }
    }
}

License

rust-native-tls is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various BSD-like licenses.

See LICENSE-APACHE, and LICENSE-MIT for details.

Comments

  • TlsAcceptor handling multiple domains/identity
    TlsAcceptor handling multiple domains/identity

    Apr 27, 2020

    How does one generate handshake for multiple DNS? I'm trying to make add tls support for a proxy server. The problem I have now is that even though I can get a TlsAcceptor to work based on the examples. I'm not so sure to understand how to get it to validate a provide different certificates for different HostName. Do I have to create an Acceptor for each domain I try to validate and then check against all of the identities until I found one that Accept the connection? Or is the Acceptor handling that itself using the PKCS12 file?

    Reply
  • Implement ALPN support
    Implement ALPN support

    May 8, 2020

    An alternative to #157 .

    Slightly more surgical in a lot of places, doesn't require an extra feature, correct error handling on macOS, and some simple tests.

    Closes #49.

    Reply
  • Support RFC 5077 TLS session ticket reuse
    Support RFC 5077 TLS session ticket reuse

    May 8, 2020

    Adds an option to enable RFC 5077 TLS session ticket reuse, and implementations for all backends.

    • The OpenSSL implementation uses set_session_cache_mode, set_new_session_callback, set_remove_session_callback, and set_session to store just the previous session that was negotiated. It doesn't look like there's a way to store any more than that, as OpenSSL doesn't pass any information about domain/host to the callbacks.
    • The Schannel implementation implements a small (10 entry) LRU cache of SchannelCred, with an expiration of 10 minutes.
    • The Security Framework implementation just calls a system API, see implementation and tests in https://github.com/kornelski/rust-security-framework/pull/95

    Also added implementation-specific unit tests for OpenSSL and Schannel. A platform-agnostic one isn't really possible because macOS doesn't expose any way of determining of the stream was successfully resumed, and we're not at a low enough abstraction level to look at the handshake process itself.

    Reply
  • Support cipher suite selection
    Support cipher suite selection

    May 11, 2020

    The exported API is implemented as an explicit whitelist with a consistent default across platforms based on the allowed ciphersuites in rust-openssl, which is itself based on the list from Python.

    The implementation for each backend is roughly what you'd expect.

    • For OpenSSL, it constructs a cartesian product of the selected algorithms, joined together with +.
    • For Schannel, it just maps the chosen algorithms to the correct Schannel equivalent.
    • For the Security Framework, it doesn't look like there's any structure behind the ciphersuite constants or more user-friendly APIs for interacting with them, so I've combed through the ciphersuite list and manually compiled lists of cipher suites that use each algorithm.

    SHA512 is not included as a possible hash algorithm because it appears to be only available in the Security Framework.

    Closes #4.

    Reply
  • allow parsing several certificates from a single pem
    allow parsing several certificates from a single pem

    May 12, 2020

    Is this something we might want to do? it would facilitate importing certificate chains.

    Not sure about the security-framework impl which I haven't tested

    Not sure how to do that with schannel, suggestions welcome

    Reply
  • Can't get add_root_certificate() to work
    Can't get add_root_certificate() to work

    Jun 4, 2020

    I'm trying to get the add_root_certificate() to work, to be able to connect to a IRC server that has a self-signed certificate. I keep getting the error below.

    To test in a local setup, I run ngircd with minimal config changes. Just with a key and cert generated like so:

    certtool --generate-privkey --outfile server-key.pem
    certtool --generate-self-signed --load-privkey server-key.pem --outfile server-cert.pem
    

    I'm here using the native-tls example code, minimally tweaked. Adding the server cert in der format which I fetch using the openssl tool.

        let mut builder = TlsConnector::builder();
    
        let mut file = File::open("localhost:6697.der").unwrap();
        let mut cert_data = vec![];
        file.read_to_end(&mut cert_data).unwrap();
        let cert = Certificate::from_der(&cert_data).unwrap();
        builder.add_root_certificate(cert);
    
        let connector: TlsConnector = builder.build().unwrap();
    
        let stream = TcpStream::connect("localhost:6697").unwrap();
        let mut stream = connector.connect("localhost", stream).unwrap();
    
        stream.write_all(b"NICK foo\n").unwrap();
        stream.write_all(b"USer a b c : d\n").unwrap();
        let mut res = vec![];
        stream.read_to_end(&mut res).unwrap();
        println!("{}", String::from_utf8_lossy(&res));
    

    Error:

    thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Failure(Ssl(Error { code: ErrorCode(1), cause: Some(Ssl(ErrorStack([Error { code: 337047686, library: "SSL routines", function: "tls_process_server_certificate", reason: "certificate verify failed", file: "ssl/statem/statem_clnt.c", line: 1913 }]))) }, X509VerifyResult { code: 20, error: "unable to get local issuer certificate" }))', src/main.rs:20:22
    

    I am able to get it to work by popping in a builder.danger_accept_invalid_certs(true).

    Reply
  • feature =
    feature = "dummy"

    May 1, 2017

    Can rust-native-tls crate have feature = "dummy"? When this option is enabled, client code can be compiled with native_tls crate, but any call results in error.

    Why.

    HTTP client or server implementation may have optional TLS. Even if it is enabled, there's runtime switch that enables TLS.

    Some people want to avoid compiling OpenSSL (or whateven implementation native-tls uses), because they don't use TLS.

    So I need to provide a feature in my library to disable TLS.

    Currently I need to have a hundred conditionals in the library code and two builds to verify that code works with and without TLS.

    If native-tls crate had an option to effectively disable TLS, I'd just forward that feature flag to native-tls crate.

    Reply
  • get client's certificate from server's TlsStream?
    get client's certificate from server's TlsStream?

    Jul 3, 2017

    Is there a way to get the client's certificate from a native_tls::TlsStream on the server's side? I couldn't find a method to do so. It seems like there should be a 'peer_certificate' accessor or something like that as there is on openssl::SslRef (available via openssl::SslStream::ssl).

    Reply
  • Unable to specify a private key / certificate chain for mutual TLS
    Unable to specify a private key / certificate chain for mutual TLS

    Apr 24, 2019

    In native-tls 0.1, it was possible to do this, as follows:

    let mut ssl =
                openssl::ssl::SslConnectorBuilder::new(openssl::ssl::SslMethod::tls()).unwrap();
    
     // Server authentication 
     ssl.set_ca_file(ca_certificate.clone()).unwrap(); 
      
     // Client authentication 
     ssl.set_private_key_file(client_key.clone(), openssl::x509::X509_FILETYPE_PEM) 
         .unwrap(); 
     ssl.set_certificate_chain_file(client_certificate.clone()) 
         .unwrap(); 
     ssl.check_private_key().unwrap(); 
      
     let builder: native_tls::TlsConnectorBuilder = 
         native_tls::backend::openssl::TlsConnectorBuilderExt::from_openssl(ssl); 
    

    It's not possible to do this in native tls 0.2, as TlsConnectorBuilderExt has been removed, and TlsConnectorBuilder doesn't implement this functionality natively.

    Reply
  • Read android certs from /system/etc/security/cacerts
    Read android certs from /system/etc/security/cacerts

    Apr 12, 2017

    This is a port of https://github.com/sfackler/rust-openssl/issues/610 to rust-native-tls.

    Reply
  • Loading pkcs12 der formatted bytes fails on macOS
    Loading pkcs12 der formatted bytes fails on macOS

    Mar 4, 2017

    This change:

    https://github.com/sfackler/rust-native-tls/commit/94a150379d42b9ee6b2d2dbbadde02cde88e5159

    To get rid of the temp file, appears to have caused an error in my builds. Rolling back this change fixes my issues. I'm running into the unwrap() on

    https://github.com/sfackler/rust-native-tls/commit/94a150379d42b9ee6b2d2dbbadde02cde88e5159#diff-9cef9dd6c1ba7275f2ad10014dd7697fR82

    I was not running into this error before the change.

    FYI: @BlameOmar

    Reply
  • Add ability to disable verification
    Add ability to disable verification

    Nov 21, 2016

    I know, I know, it's horrible. It is also a reality that many people deal with this, and so it would be useful to add a way to disable verification. It can and probably should be a terrifying long name, very_insecure_disable_verification or what-have-you.

    cc https://github.com/seanmonstar/reqwest/issues/15

    Reply