AES GCM symmetric encryption of data at rest in c#, ciphertext much longer than plaintext

I've written encryption/decryption routines using AES GCM. Code can be found here.

I recently realized that the cipher text is longer than I believe it should be. It should add the tag and IV onto the encrypted plaintext, but the result is still longer than it should be.

Here is the portion of the code doing the encrypting

    using (MemoryStream ms = new MemoryStream())
{
using (IAuthenticatedCryptoTransform encryptor = aes.CreateAuthenticatedEncryptor())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))

{
// Write through and retrieve encrypted data.
cs.Write(message, 0, message.Length);
cs.FlushFinalBlock();
byte[] cipherText = ms.ToArray();

// Retrieve tag and create array to hold encrypted data.
byte[] authenticationTag = encryptor.GetTag();
byte[] encrypted = new byte[cipherText.Length + aes.IV.Length + authenticationTag.Length];

// Set needed data in byte array.
aes.IV.CopyTo(encrypted, 0);
authenticationTag.CopyTo(encrypted, IV_LENGTH);
cipherText.CopyTo(encrypted, IV_LENGTH + TAG_LENGTH);

// Store encrypted value in base 64.
return Convert.ToBase64String(encrypted);
}
}
}


Here are two examples of the encryption:

plaintext: example

ciphertext: XtmTBIqKxKdYKWH2zXRUp8jN6etGNUiTyffAFZYV3KB2WVU=

plaintext: much longer example, blah blah blah blah blah

ciphertext: H17hnG4CSmZQ0UeEcY6wirtjW1il+dw7JHqXwWm908Tvb8/+q0E1HerN0chbuUbhL0jLOs8HIpp7ypQQ/ LTacnWW22CyiwAuiA==

The IV is 12 bytes. And I believe the authentication tag is 16 bytes. Why is the cipher text coming out so much longer than expected and how should I fix this issue? Thank you.

-
You have IV, data, maybe padding, authentication, and then increase the whole stuff by factor 4/3 through your base-64-encoding. But I think this is not quite the right site, it looks like a better fit for our sister site Stack Overflow. (We can migrate it there for you, but I'll have to go now.) –  Paŭlo Ebermann Jul 27 '12 at 16:20
Although the question does talk about specifics (and therefore might belong elsewhere) it mainly deals with the general concept of cipher text vs plaintext length so I figured I'd try here. If it should be elsewhere feel free to migrate it. –  crawfish Jul 27 '12 at 16:25

1 Answer

If you go through the math, it appears that exactly the expected amount of ciphertext expansion is happening. Here's what's happening:

• The GCM takes the plaintext as a byte string of size N, and generates a ciphertext which is a byte string of size N+28, where 12 of the 28 is the nonce, and the other 16 is the authentication tag.

• Then, that octet string undergoes a base-64 encoding, which converts 3 bytes into 4 printable characters; this gives an average ciphertext expansion of a factor of 4/3.

In the first case, you had a plaintext of 7 bytes; after encryption, that gives you a ciphertext of 35 bytes; the base-64 encoding of these 35 bytes would be expected to be 47 bytes long, which is exactly what you see.

In the second case, you had a plaintext of 46 bytes (assuming there's a trailing space to the string you gave); after encryption, that gives you a ciphertext of 74 bytes; the base-64 encoding is then 99 bytes, which is exactly what you see.

Now, you ask how you should fix this issue. Well, the transform is working as designed; what is the issue? If the issue was that you were concerned that something wasn't working properly, well, everything appears to be correct. If the issue was that the ciphertext was too long for your application, well, you'd need to consider how to trim it. Here are some easy ways:

• Do you really need the output to be printable? Well, I see you put the 'database' tag on the question; are you encrypting a database field? If so, can the database store arbitrary bytes, or is it restricted only to printable strings? If it can store arbitrary bytes, then you can get rid of a good part of the overhead simply by not base-64 encoding the text. If you do, well, if you're desperate, there are some more aggressive encoding methods (which use more characters in their outputs); going to one of those would save you some small amount.
-
Great answer, thank you. I didn't realize the base64 added to the length to the extent that it does. –  crawfish Jul 27 '12 at 18:53