Ответ 1
Вы можете использовать ECDiffieHellman для шифрования сообщений. У вас есть два варианта: статический статический ECDH и статический эфемерный ECDH:
Для статического статического ECDH получателю необходимо знать открытый ключ отправителей (это может быть или не быть вариантом в вашем приложении). Вы также должны иметь некоторые данные, которые уникальны для этого сообщения (это может быть серийный номер, который вы получаете из другого места в протоколе или строке базы данных или что-то еще, или это может быть nonce). Затем вы используете ECDH для создания секретного ключа и используете его для шифрования ваших данных. Это даст вам желаемую зашифрованную длину данных 16 байт, но она не является полностью асимметричной: шифр также способен расшифровывать сообщения (опять же: это может быть или не быть проблемой в вашем приложении).
Static-ephemeral немного отличается: здесь шифр генерирует временную (эфемерную) ключевую пару EC. Затем он использует эту пару ключей вместе с открытым ключом приемников для создания секретного ключа, который может использоваться для шифрования данных. Наконец, он отправляет открытый ключ эфемерной пары ключей в приемник вместе с зашифрованными данными. Это может поместиться лучше в ваше приложение, но теперь все зашифрованные данные будут 2 * 32 + 16 = 80 байт с использованием ECDH-256 и AES (как отмечает GregS, вы можете сохранить 32 байта, отправив только координату x публично- но я не верю, что .NET предоставляет функции для пересчета y-координаты).
Вот небольшой класс, который будет выполнять статический статический ECDH:
public static class StaticStaticDiffieHellman
{
private static Aes DeriveKeyAndIv(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce)
{
privateKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
privateKey.HashAlgorithm = CngAlgorithm.Sha256;
privateKey.SecretAppend = nonce;
byte[] keyAndIv = privateKey.DeriveKeyMaterial(publicKey);
byte[] key = new byte[16];
Array.Copy(keyAndIv, 0, key, 0, 16);
byte[] iv = new byte[16];
Array.Copy(keyAndIv, 16, iv, 0, 16);
Aes aes = new AesManaged();
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
return aes;
}
public static byte[] Encrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] data){
Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);
return aes.CreateEncryptor().TransformFinalBlock(data, 0, data.Length);
}
public static byte[] Decrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] encryptedData){
Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);
return aes.CreateDecryptor().TransformFinalBlock(encryptedData,0, encryptedData.Length);
}
}
// Usage:
ECDiffieHellmanCng key1 = new ECDiffieHellmanCng();
ECDiffieHellmanCng key2 = new ECDiffieHellmanCng();
byte[] data = Encoding.UTF8.GetBytes("TestTestTestTes");
byte[] nonce = Encoding.UTF8.GetBytes("whatever");
byte[] encryptedData = StaticStaticDiffieHellman.Encrypt(key1, key2.PublicKey, nonce, data);
Console.WriteLine(encryptedData.Length); // 16
byte[] decryptedData = StaticStaticDiffieHellman.Decrypt(key2, key1.PublicKey, nonce, encryptedData);
Console.WriteLine(Encoding.UTF8.GetString(decryptedData));