Server
This commit is contained in:
181
Encryption.cs
Normal file
181
Encryption.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user