views:

80

answers:

2

I'd like to encrypt very little data (15 bytes to be exact) into a as short as possible (optimally, no longer than 16 bytes) message using a public key cryptography system.

The standard public key system, RSA, unfortunately produces messages as big as its keys, that is about 100 bytes, depending on key size. To make things more difficult, I can only use .NET framework libraries, i.e. no third party.

I've read a little about elliptic curve cryptography in the wikipedia and the text there seems to suggest that key sizes there are usually much shorter than RSA keys.

Does this translate to short messages as well? Can the .NET ECDiffieHellmanCng class be used to de/encrypt messages? It seems to feature different class structure then, say, RSA or the symmetric ciphers.

A: 

ECDiffieHellmanCNG is a derivation of the original Diffie-Hellman Key Exchange Protocol.
It is not intended for encrypting messages but rather calculating the same secret value on both ends.

Here is some information on ECDiffieHellmanCNG and its purpose.

Sani Huttunen
Thanks for the input! I was hoping that I could somehow misuse the algorithm used in this class for my purpose. =)
Jens
+5  A: 

You can use ECDiffieHellman to encrypt messages. You have two options: Static-static ECDH and static-ephemeral ECDH:

For static-static ECDH the receiver will need to know the senders public key (this might or might not be an option in your application). You should also have some data that is unique for this message (it might be a serial-number you get from somewhere else in the protocol or database-row or whatever or it might be a nonce). You then use ECDH to generate a secret key and use that to encrypt your data. This will give you your desired encrypted data length of 16 bytes, but it is not completely asymmetric: the encryptor is also able to decrypt the messages (again: this might or might not be a problem in your application).

Static-ephemeral is a bit different: here the encryptor generates a temporary (ephemeral) EC keypair. He then uses this keypair together with the receivers public key to generate a secret key which can be used to encrypt the data. Finally he sends the public key of the ephemeral keypair to the receiver together with the encrypted data. This might fit better into your application, but the complete encrypted data will now be 2*32+16=80 bytes using ECDH-256 and AES (as GregS notes you can save 32 bytes by only sending the x-coordinate of the public-key, but I do not believe that .NET exposes the functionality to recalculate the y-coordinate).

Here is a small class that will do static-static 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));
Rasmus Faber
You can save 32 bytes by only sending the x coordinate of the ephemeral public key.
GregS
Thanks! The static-static ECDH seems to be what I was looking for. Do you happen to know if and how I can use the .NET ECDH classes to encrypt a message? As Sani pointed out, this does not seem to be their primary function.
Jens
@Jens: I have added a small example
Rasmus Faber
Thanks much! This will be helpful!
Jens
@Rasmus Faber: I think you are right, you'd have to add a lot of your own code to compute the y-coordinate. Some systems support the "compressed" form; maybe .NET does too in some unadvertised way. The .NET documentation is not very good IMHO.
GregS