namespace GeoSus.Server; using System.Security.Cryptography; using System.Text; // Šifrování komunikace - RSA handshake + AES-256-CBC session (kompatibilní s Unity) public class ServerEncryption : IDisposable { private readonly RSA _rsa; private readonly string _publicKeyPem; public ServerEncryption(int keySizeBits = 2048) { _rsa = RSA.Create(keySizeBits); _publicKeyPem = ExportPublicKeyPem(); } public string PublicKeyPem => _publicKeyPem; // Dešifruje session key od klienta public (byte[] Key, byte[] IV) DecryptSessionKey(string encryptedKeyBase64, string encryptedIvBase64) { var encryptedKey = Convert.FromBase64String(encryptedKeyBase64); var encryptedIv = Convert.FromBase64String(encryptedIvBase64); // Používáme OaepSHA1 pro Unity kompatibilitu var key = _rsa.Decrypt(encryptedKey, RSAEncryptionPadding.OaepSHA1); var iv = _rsa.Decrypt(encryptedIv, RSAEncryptionPadding.OaepSHA1); return (key, iv); } private string ExportPublicKeyPem() { var publicKey = _rsa.ExportSubjectPublicKeyInfo(); var base64 = Convert.ToBase64String(publicKey); var sb = new StringBuilder(); sb.AppendLine("-----BEGIN PUBLIC KEY-----"); for (int i = 0; i < base64.Length; i += 64) { sb.AppendLine(base64.Substring(i, Math.Min(64, base64.Length - i))); } sb.AppendLine("-----END PUBLIC KEY-----"); return sb.ToString(); } public void Dispose() { _rsa.Dispose(); } } // Session šifrování pro konkrétní klientské spojení - AES-256-CBC + HMAC-SHA256 public class SessionCrypto : IDisposable { private readonly byte[] _key; private readonly object _lock = new(); public SessionCrypto(byte[] key, byte[] iv) { if (key.Length != 32) throw new ArgumentException("Key musí být 32 bajtů pro AES-256"); if (iv.Length != 16) throw new ArgumentException("IV musí být 16 bajtů pro AES-CBC"); _key = key; // IV se nepoužívá přímo - každá zpráva má svůj unikátní IV } // Šifruje zprávu s AES-CBC a HMAC public byte[] Encrypt(byte[] plaintext) { lock (_lock) { using var aes = Aes.Create(); aes.Key = _key; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; aes.GenerateIV(); // Generujeme nový IV pro každou zprávu byte[] ciphertext; using (var encryptor = aes.CreateEncryptor()) { ciphertext = encryptor.TransformFinalBlock(plaintext, 0, plaintext.Length); } // Počítáme HMAC přes IV + ciphertext (používáme AES klíč pro HMAC) byte[] hmac; using (var hmacSha = new HMACSHA256(_key)) { var dataToSign = new byte[aes.IV.Length + ciphertext.Length]; Buffer.BlockCopy(aes.IV, 0, dataToSign, 0, aes.IV.Length); Buffer.BlockCopy(ciphertext, 0, dataToSign, aes.IV.Length, ciphertext.Length); hmac = hmacSha.ComputeHash(dataToSign); } // Výstup: [16 bytes IV][32 bytes HMAC][ciphertext] var result = new byte[16 + 32 + ciphertext.Length]; Buffer.BlockCopy(aes.IV, 0, result, 0, 16); Buffer.BlockCopy(hmac, 0, result, 16, 32); Buffer.BlockCopy(ciphertext, 0, result, 48, ciphertext.Length); return result; } } // Dešifruje zprávu a validuje HMAC public byte[]? Decrypt(byte[] encrypted) { if (encrypted.Length < 48) return null; // 16 IV + 32 HMAC + min data try { var iv = new byte[16]; var receivedHmac = new byte[32]; var ciphertext = new byte[encrypted.Length - 48]; Buffer.BlockCopy(encrypted, 0, iv, 0, 16); Buffer.BlockCopy(encrypted, 16, receivedHmac, 0, 32); Buffer.BlockCopy(encrypted, 48, ciphertext, 0, ciphertext.Length); // Ověříme HMAC (používáme AES klíč pro HMAC) byte[] computedHmac; using (var hmacSha = new HMACSHA256(_key)) { var dataToVerify = new byte[16 + ciphertext.Length]; Buffer.BlockCopy(iv, 0, dataToVerify, 0, 16); Buffer.BlockCopy(ciphertext, 0, dataToVerify, 16, ciphertext.Length); computedHmac = hmacSha.ComputeHash(dataToVerify); } // Constant-time porovnání if (!CryptographicOperations.FixedTimeEquals(receivedHmac, computedHmac)) { return null; // HMAC mismatch - zpráva byla změněna } // Dešifrujeme using var aes = Aes.Create(); aes.Key = _key; aes.IV = iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using var decryptor = aes.CreateDecryptor(); return decryptor.TransformFinalBlock(ciphertext, 0, ciphertext.Length); } catch (CryptographicException) { return null; } } public void Dispose() { Array.Clear(_key, 0, _key.Length); } } // Klientská strana - generuje session key a šifruje ho RSA public key public static class ClientEncryptionHelper { public static (byte[] Key, byte[] IV) GenerateSessionKey() { var key = RandomNumberGenerator.GetBytes(32); // AES-256 var iv = RandomNumberGenerator.GetBytes(16); // AES-CBC IV (16 bytes) return (key, iv); } public static (string EncryptedKey, string EncryptedIV) EncryptSessionKey( string rsaPublicKeyPem, byte[] key, byte[] iv) { using var rsa = RSA.Create(); rsa.ImportFromPem(rsaPublicKeyPem); var encryptedKey = rsa.Encrypt(key, RSAEncryptionPadding.OaepSHA256); var encryptedIv = rsa.Encrypt(iv, RSAEncryptionPadding.OaepSHA256); return (Convert.ToBase64String(encryptedKey), Convert.ToBase64String(encryptedIv)); } }