← Back to Projects

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.

DiffieHellman.cs
ECDH key generation, AES encrypt/decrypt
SecureKeyExchange
Wraps DH — exposes public key + IV
SocketClient/Program.cs
TCP listener/connector + Playfair mode

ECDH + AES Handshake Flow

1
Each party generates an EC key pair
On startup, 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.
2
Server sends its public key + IV over the socket
The server serializes its public key and AES initialisation vector as a JSON object and writes it to the NetworkStream. No secret has been transmitted yet.
3
Both sides derive the same shared secret
Each party calls 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.
4
Client encrypts messages with AES using the derived key
Every message is encrypted with 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

DiffieHellman.cs — constructor
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();
}
DiffieHellman.cs — EncryptString()
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();
    }
}
Program.cs — server handshake + decrypt loop
// 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.

CustomOperation.cs — shift + Playfair pipeline
// 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.

Prepared input: HELXLO(doubled letters split with X; padded to even length)
Output
ISKYIQ
5×5 Key Square — key "SECRET", standard A–Z alphabet (I=J)
S
E
C
R
T
A
B
D
F
G
H
I
K
L
M
N
O
P
Q
U
V
W
X
Y
Z

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.