Минимальное шифрование открытого ключа размера сообщения в .NET.

Я хотел бы зашифровать очень мало данных (точнее, 15 байт) в кратчайшее (возможно, не более 16 байт) сообщение с использованием криптографической системы с открытым ключом.

Стандартная система открытых ключей RSA, к сожалению, производит сообщения размером с его ключи, что составляет около 100 байт, в зависимости от размера ключа. Чтобы усложнить ситуацию, я могу использовать только библиотеки .NET framework, т.е. Сторонних.

Я немного читал о криптографии с кривыми эллиптической кривой в википедии, и текст там, кажется, предполагает, что размеры клавиш там обычно намного короче, чем ключи RSA.

Переводит ли это также короткие сообщения? Можно ли использовать класс .NET ECDiffieHellmanCng для де-шифрования сообщений? Кажется, что он имеет другую структуру классов, то, скажем, RSA или симметричные шифры.

Ответ 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));