Socket Client Encryption
TCP client/server messaging with three layers of cryptography: Elliptic Curve Diffie-Hellman key exchange, AES-256 encryption, and a Playfair classical cipher bonus mode — all implemented from scratch in C# .NET.
Architecture
The project is a single .NET console binary that runs as either a server or client depending on your input. It has three components: a DiffieHellman key exchange library, a SecureKeyExchange wrapper, and a SocketClient entry point. A hidden third mode (v) runs a standalone Playfair cipher demonstration.
ECDH + AES Handshake Flow
ECDiffieHellmanCng generates a private/public elliptic-curve key pair using the Windows CNG API. The public key is exported as a byte array — this is safe to transmit over plaintext.NetworkStream. No secret has been transmitted yet.DeriveKeyMaterial(otherPublicKey)which runs SHA-256 over the ECDH shared point. Both parties arrive at the same 256-bit key without it ever crossing the network — this is the core of Diffie-Hellman.AesCryptoServiceProvider using the derived shared key. The ciphertext + IV are sent as JSON. The server decrypts using its own copy of the shared key and prints both versions.Key Code
public DiffieHellman()
{
this.aes = new AesCryptoServiceProvider();
this.diffieHellman = new ECDiffieHellmanCng
{
KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash,
HashAlgorithm = CngAlgorithm.Sha256
};
// This is the public key we send to the other party
this.publicKey = this.diffieHellman.PublicKey.ToByteArray();
}public byte[] EncryptString(string secretMessage, byte[] publicKey)
{
var key = CngKey.Import(publicKey, CngKeyBlobFormat.EccPublicBlob);
var derivedKey = this.diffieHellman.DeriveKeyMaterial(key); // Shared secret
this.aes.Key = derivedKey;
using (var cipherText = new MemoryStream())
using (var encryptor = this.aes.CreateEncryptor())
using (var cryptoStream = new CryptoStream(cipherText, encryptor, CryptoStreamMode.Write))
{
byte[] plainBytes = Encoding.UTF8.GetBytes(secretMessage);
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
return cipherText.ToArray();
}
}// Server sends its public key + IV as JSON
var obj = new {
publicKey = JsonConvert.SerializeObject(dh.PublicKey.Select(b => (int)b).ToArray()),
IV = JsonConvert.SerializeObject(dh.IV.Select(b => (int)b).ToArray())
};
ns.Write(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(obj)));
// Then decrypts every incoming message
var decrypted = dh.DecryptString(dataBytes, dh.PublicKey, IVDataBytes);
Console.WriteLine("Encrypted: " + text);
Console.WriteLine("Decrypted: " + decrypted);Bonus Mode: Playfair Cipher
Accessible via mode v, this mode runs a three-step classical cipher pipeline: first a shift + reverse on the ASCII values, then a full Playfair cipher (5×5 key square, digraph substitution) using a user-supplied key, then deciphers the result with a hardcoded key as a self-test.
// 1. Shift ASCII values down by 40, wrap at 126, then reverse
List<byte> bytes = Encoding.ASCII.GetBytes(pass).ToList();
var shifted = bytes.Select(x => {
var newValue = x - modifier; // shift down
var lappedValue = upper - newValue; // wrap around
return (newValue > upper) ? lappedValue : newValue;
}).ToList();
shifted.Reverse();
// 2. Encipher with Playfair using the provided key
var enciphered = Encipher(passAsString, keyAsString);
// 3. Decipher back (hardcoded key "Buger" — self-test)
var deciphered = Decipher(enciphered, "Buger");Live Playfair Cipher
This is the same algorithm from CustomOperation.cs, ported to TypeScript. The key square is generated by prepending the key to the full character set and removing duplicates.
Note: the C# original uses an extended 82-character alphabet (symbols + digits + mixed case) — this demo uses the canonical A–Z version for clarity.
Companion: Socket Client/Server (Plain)
An earlier, simpler version (SocketProjectClientServer) is included in the source. It is a WPF GUI client that connects to a hardcoded LAN IP and sends plain-text messages over TCP — no encryption, no key exchange. Both versions are in the canonical source folder for comparison.