Hello there, fellow tech enthusiast! 🦀
If you’ve been itching to give your servers a unique secret handshake that only they understand, you’ve come to the right place. Today, we’re venturing into the world of secure server-to-server handshakes, using the powerful combo of Rust and OpenSSL.
Why Choose Rust & OpenSSL?
First off, Rust is like that reliable friend you can always count on. It promises safety without compromising on speed, making it a favorite for projects like ours. On the other side, we have OpenSSL, a time-tested toolkit for ensuring secure communications over digital networks. Combine these two, and you’ve got yourself a dynamic duo!
The openssl Crate in the Limelight
The openssl crate serves as a bridge between the Rust programming language and the OpenSSL library. OpenSSL itself is a titan in the world of security, boasting a storied history spanning decades. Given its crucial role in web security, having a performant and type-safe interface in Rust is immensely valuable. The openssl crate is more than just a wrapper; it integrates OpenSSL's raw power into the Rust paradigm, ensuring memory safety and concurrency benefits.
Deep Dive into Features
- Cryptography:
- Symmetric Encryption: Using algorithms like AES, it supports encryption where the same key is used for both encryption and decryption.
- Asymmetric Encryption: Enables public-private key pair operations with algorithms such as RSA. In this scheme, a public key encrypts data, while a private key decrypts it.
- Message Digests and Hashing: Offers tools for creating digests of data, ensuring data integrity using algorithms like SHA256, MD5, and more.
- Digital Signatures: Validates data authenticity and integrity. Using a private key, one can sign data, and using the corresponding public key, others can verify the signature.
2. SSL/TLS Framework:
- Connection Handling: Initiates and manages both client and server-side connections, facilitating encrypted communication.
- Context Configuration: Manages settings and callbacks for groups of connections, ensuring flexibility in handling SSL/TLS parameters.
- Session Resumption: Speeds up the TLS handshake process by reusing session parameters, optimizing connection times.
3. X.509 Certificate Management:
- Certificate Generation and Inspection: Allows for the creation of self-signed certificates and inspection of various certificate fields.
- Certificate Verification: Validates the authenticity of a certificate against a set of trusted certificates.
- Certificate Chains and Stores: Manages chains of certificates and stores of trusted CAs.
4. Key Management and Generation:
- RSA, ECDSA, and DSA: Facilitates operations around various key types, from generation to serialization.
- Private Key Security: Ensures private keys are kept secure, supporting both traditional PEM and the more secure PKCS8 formats.
5. ASN.1 and DER Functionality:
- ASN.1 (Abstract Syntax Notation One) is the language used to define data structures.
- DER (Distinguished Encoding Rules) is a specific method to encode ASN.1 data structures. This encoding is foundational in how certificates and keys are represented.
6. Custom Extensions and Plugins: The crate is extensible, allowing for the integration of custom cryptographic methods and extensions, ensuring that developers are not limited by the provided set of features.
Getting the Ball Rolling:
1. The Checklist:
- A dash of Rust knowledge.
- Rust and Cargo, ready to roll on your machine.
- OpenSSL library, all set up.
2. Let’s Set Up Camp:
Kick things off with a brand new Rust project:
cargo new secure_handshake
cd secure_handshake
3. The Right Tools for the Job:
Add in the crucial libraries to your Cargo.toml:
[dependencies]
tokio = { version = "1", features = ["full"] }
openssl = "0.10"
tokio_openssl = "0.6"
4. Crafting the Digital Keys:
Let’s make some self-signed certificates.
At this point, I suggest you jump straight to the Rust’s Hands-On Digital Keys Generator we developed together on this article.
That will give your a much more in-depth understanding of how certificates and keys are generated at a low level.
Alternatively, if you don’t have the time now to read it, feel free to generate your keys manually by running:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
Dive Right Into the Code:
1. Building Our Server’s Sanctuary:
Here, we’re setting up a server to handle the secret handshakes securely:
async fn run_server() {
let mut server_config = ServerConfig::new(NoClientAuth::new());
server_config.set_single_cert(load_certs(), load_private_key()).unwrap();
let server_config = Arc::new(server_config);
let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
let acceptor = TlsAcceptor::from(server_config);
loop {
let (stream, _) = listener.accept().await.unwrap();
let tls_stream = acceptor.accept(stream).await.unwrap();
tokio::spawn(handle_client(tls_stream));
}
}
async fn handle_client(mut tls_stream: tokio_rustls::server::TlsStream<TcpStream>) {
// Reads data from the TLS stream
let mut data = vec![0; 100];
tls_stream.read(&mut data).await.unwrap();
if &data[..5] == b"HELLO" {
tls_stream.write_all(b"HELLO_ACK").await.unwrap();
println!("Received HELLO from client. Sent HELLO_ACK.");
}
// You can add more exchanges here.
}



