Hello.
How to use Ed25519 keys with tokio rustls?
I would like to use that examples but with Ed25519 keys.
I need help with writing examples (client and server with Ed25519).
Server:
use rustls_pemfile::{certs, pkcs8_private_keys};
use std::fs::File;
use std::io::{self, BufReader};
use std::path::{Path};
use std::sync::Arc;
use tokio::io::{copy, sink, split, AsyncWriteExt};
use tokio::net::TcpListener;
use tokio_rustls::rustls::{self, Certificate, PrivateKey};
use tokio_rustls::TlsAcceptor;
fn load_certs(path: &Path) -> io::Result<Vec<Certificate>> {
certs(&mut BufReader::new(File::open(path)?))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))
.map(|mut certs| certs.drain(..).map(Certificate).collect())
}
fn load_keys(path: &Path) -> io::Result<Vec<PrivateKey>> {
pkcs8_private_keys(&mut BufReader::new(File::open(path)?))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))
.map(|mut keys| keys.drain(..).map(PrivateKey).collect())
}
#[tokio::main]
async fn main() -> io::Result<()> {
// Generate cert from private_key:
//
// openssl req -nodes \
// -x509 \
// -key Ed25519_private_key.pem \
// -out eddsa-ca.cert \
// -sha256 \
// -batch \
// -days 3650 \
// -subj "/CN=ponytown EdDSA CA"
let private_key_path = Path::new("Ed25519_private_key.pem");
let cert_path = Path::new("eddsa-ca.cert");
let certs = load_certs(&cert_path)?;
let keys = load_keys(&private_key_path)?;
println!("{:?}", certs);
println!("{:?}", keys);
let flag_echo = true;
let private_key = keys.get(0).unwrap().clone();
let config = rustls::ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(certs, private_key)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
let acceptor = TlsAcceptor::from(Arc::new(config));
let listener = TcpListener::bind("127.0.0.1:8888").await?;
loop {
let (stream, peer_addr) = listener.accept().await?;
let acceptor = acceptor.clone();
let fut = async move {
let mut stream = acceptor.accept(stream).await?;
if flag_echo {
let (mut reader, mut writer) = split(stream);
let n = copy(&mut reader, &mut writer).await?;
writer.flush().await?;
println!("Echo: {} - {}", peer_addr, n);
}
else
{
let mut output = sink();
stream
.write_all(
&b"HTTP/1.0 200 ok\r\n\
Connection: close\r\n\
Content-length: 12\r\n\
\r\n\
Hello world!"[..],
)
.await?;
stream.shutdown().await?;
copy(&mut stream, &mut output).await?;
println!("Hello: {}", peer_addr);
}
Ok(()) as io::Result<()>
};
tokio::spawn(async move {
if let Err(err) = fut.await {
eprintln!("{:?}", err);
}
});
}
}
Client:
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::sync::Arc;
use tokio::io::{copy, split, stdin as tokio_stdin, stdout as tokio_stdout, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio_rustls::{TlsConnector};
use rustls_pemfile::{certs};
use tokio_rustls::rustls::{Certificate};
use std::path::{Path};
use tokio_rustls::rustls::{ClientConfig, ServerName, RootCertStore};
use std::convert::TryFrom;
fn load_certs(path: &Path) -> io::Result<Vec<Certificate>> {
certs(&mut BufReader::new(File::open(path)?))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))
.map(|mut certs| certs.drain(..).map(Certificate).collect())
}
#[tokio::main]
async fn main() -> io::Result<()> {
let host = "127.0.0.1";
let port = "8888";
let addr = format!("{}:{}", host, port);
let cert_path = Path::new("eddsa-ca.cert");
let certs = load_certs(&cert_path)?;
println!("{:?}", certs);
let cert = certs.get(0).unwrap().clone();
let mut root_store = RootCertStore::empty();
root_store.add(&cert).unwrap();
let config = ClientConfig::builder()
.with_safe_default_cipher_suites()
.with_safe_default_kx_groups()
.with_safe_default_protocol_versions()
.unwrap()
.with_root_certificates(root_store)
.with_no_client_auth();
let connector = TlsConnector::from(Arc::new(config));
let stream = TcpStream::connect(&addr).await?;
let (mut stdin, mut stdout) = (tokio_stdin(), tokio_stdout());
let domain = ServerName::try_from(host)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid dnsname"))?;
let content = "HELLO WORLD";
let mut stream = connector.connect(domain, stream).await?;
stream.write_all(content.as_bytes()).await?;
let (mut reader, mut writer) = split(stream);
tokio::select! {
ret = copy(&mut reader, &mut stdout) => {
ret?;
},
ret = copy(&mut stdin, &mut writer) => {
ret?;
writer.shutdown().await?
}
}
Ok(())
}
Server output:
[Certificate(b"0\x82\x01L0\x81\xff\xa0\x03\x02\x01\x02\x02\x14\x17\xbc\xb7\xf2\x9b\xeb\x9a jI\x88O\xd20q\xe2{\xc0\xff\xa10\x05\x06\x03+ep0\x1c1\x1a0\x18\x06\x03U\x04\x03\x0c\x11ponytown EdDSA CA0\x1e\x17\r220820130336Z\x17\r320817130336Z0\x1c1\x1a0\x18\x06\x03U\x04\x03\x0c\x11ponytown EdDSA CA0*0\x05\x06\x03+ep\x03!\0s\x7fSA_\xe4`\xd2\xda\xda\xb9ghV>\xfba\xb6!E\x93b\x96\xe0\xfeMc\xf57\x82\x82W\xa3S0Q0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xf5\x14\xfe\x05\xc2\xe1_\xff\xe8\xacFx\x1f\xf8\x97Y,\xbb\xc5\xcf0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\xf5\x14\xfe\x05\xc2\xe1_\xff\xe8\xacFx\x1f\xf8\x97Y,\xbb\xc5\xcf0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\x05\x06\x03+ep\x03A\0\xb7\x0e\xac\xbbE\x8c\x04\xbd\xbe\x8e\x02\xb3B7\xf8\xfb8\xef\xfe8\xf19\x9a 2g\xe9$\xe2\xe9s&\xd5\xbc\x8b\x9b.\xdf\xbcW4)\x1e\xa9\xd0K\xb4\x82\xa8\x1d\x01\x1f44!\xdb\xa0!61\xa1\x01y\x02")]
[PrivateKey([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 45, 107, 131, 28, 150, 11, 0, 12, 157, 125, 138, 145, 239, 74, 76, 190, 103, 162, 38, 81, 180, 157, 179, 29, 83, 83, 254, 194, 36, 252, 180, 7])]
Custom { kind: InvalidData, error: AlertReceived(BadCertificate) }
Client output:
[Certificate(b"0\x82\x01L0\x81\xff\xa0\x03\x02\x01\x02\x02\x14\x17\xbc\xb7\xf2\x9b\xeb\x9a jI\x88O\xd20q\xe2{\xc0\xff\xa10\x05\x06\x03+ep0\x1c1\x1a0\x18\x06\x03U\x04\x03\x0c\x11ponytown EdDSA CA0\x1e\x17\r220820130336Z\x17\r320817130336Z0\x1c1\x1a0\x18\x06\x03U\x04\x03\x0c\x11ponytown EdDSA CA0*0\x05\x06\x03+ep\x03!\0s\x7fSA_\xe4`\xd2\xda\xda\xb9ghV>\xfba\xb6!E\x93b\x96\xe0\xfeMc\xf57\x82\x82W\xa3S0Q0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xf5\x14\xfe\x05\xc2\xe1_\xff\xe8\xacFx\x1f\xf8\x97Y,\xbb\xc5\xcf0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\xf5\x14\xfe\x05\xc2\xe1_\xff\xe8\xacFx\x1f\xf8\x97Y,\xbb\xc5\xcf0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\x05\x06\x03+ep\x03A\0\xb7\x0e\xac\xbbE\x8c\x04\xbd\xbe\x8e\x02\xb3B7\xf8\xfb8\xef\xfe8\xf19\x9a 2g\xe9$\xe2\xe9s&\xd5\xbc\x8b\x9b.\xdf\xbcW4)\x1e\xa9\xd0K\xb4\x82\xa8\x1d\x01\x1f44!\xdb\xa0!61\xa1\x01y\x02")]
Error: Custom { kind: InvalidData, error: UnsupportedNameType }
Ed25519 Keys (generated by jwt_simple)
Ed25519_pair_key.pem:
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIC1rgxyWCwAMnX2Kke9KTL5noiZRtJ2zHVNT/sIk/LQH
-----END PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAc39TQV/kYNLa2rlnaFY++2G2IUWTYpbg/k1j9TeCglc=
-----END PUBLIC KEY-----
Ed25519_public_key.pem:
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAc39TQV/kYNLa2rlnaFY++2G2IUWTYpbg/k1j9TeCglc=
-----END PUBLIC KEY-----
eddsa-ca.cert:
Generated by: openssl req -nodes -x509 -key Ed25519_private_key.pem -out eddsa-ca.cert -sha256 -batch -days 3650 -subj "/CN=ponytown EdDSA CA"
-----BEGIN CERTIFICATE-----
MIIBTDCB/6ADAgECAhQXvLfym+uaIGpJiE/SMHHie8D/oTAFBgMrZXAwHDEaMBgG
A1UEAwwRcG9ueXRvd24gRWREU0EgQ0EwHhcNMjIwODIwMTMwMzM2WhcNMzIwODE3
MTMwMzM2WjAcMRowGAYDVQQDDBFwb255dG93biBFZERTQSBDQTAqMAUGAytlcAMh
AHN/U0Ff5GDS2tq5Z2hWPvthtiFFk2KW4P5NY/U3goJXo1MwUTAdBgNVHQ4EFgQU
9RT+BcLhX//orEZ4H/iXWSy7xc8wHwYDVR0jBBgwFoAU9RT+BcLhX//orEZ4H/iX
WSy7xc8wDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQC3Dqy7RYwEvb6OArNCN/j7
OO/+OPE5miAyZ+kk4ulzJtW8i5su37xXNCkeqdBLtIKoHQEfNDQh26AhNjGhAXkC
-----END CERTIFICATE-----