← blog
May 10, 2026

Building encrypted file transfer over Bluetooth and LAN in C#

How Network Transfer works under the hood: AES-256-GCM encryption, the Bluetooth handshake, SHA-256 transfer verification, and why I built it instead of using Windows' built-in tools.

C#.NETNetworkingBluetoothSecurity

Network Transfer came from a specific problem: I wanted to move files between two Windows machines on the same desk without a USB drive or a shared folder. AirDrop exists but only on Apple. Windows Nearby Sharing exists but requires Microsoft accounts and does not always work. So I built something.

The encryption side

The core of it is a TCP socket session with AES-256-GCM encryption. When a transfer starts, the sender generates a random session key and derives a per-file encryption key from it using HKDF. The receiver gets the session key via the initial handshake.

csharp
// Key derivation per file
var info = Encoding.UTF8.GetBytes($"file:{filename}:{fileIndex}");
var fileKey = HKDF.DeriveKey(
    HashAlgorithmName.SHA256,
    sessionKey,
    outputLength: 32,
    salt: salt,
    info: info
);
using var aes = new AesGcm(fileKey, tagSizeInBytes: 16);

The Bluetooth handshake

The Bluetooth side uses Windows.Devices.Bluetooth.Rfcomm from the Windows Runtime APIs. WinRT is available in .NET 5+ on Windows, which was one of the reasons for targeting .NET 8. The handshake goes over RFCOMM, transfers go over TCP (LAN mode) or a Bluetooth socket (BT mode).

Transfer verification

After every transfer finishes, the receiver computes a SHA-256 hash of the received file and sends it back to the sender. If the hashes do not match, the transfer is flagged as corrupted and can be retried. In practice this has never triggered on LAN. The failure mode it protects against is transmission corruption on flaky Bluetooth connections.

What is not built yet

Auto-discovery. If both machines are on the same subnet, you still need to enter the IP manually for LAN mode. Bluetooth does not need this because the handshake uses the device picker. Auto-discovery via mDNS is on the list but has not made it in yet. The current workaround is just typing the local IP, which is a one-time thing per machine.

← all postsprojects