Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I am trying to implement a AES 256 encryption with GCM using BouncyCastle library.
So far I have managed to make it work by passing
Key and Nonce
as string and
Tag
as byte array.
This is the encryption method.
private static byte[] EncryptWithGCM(string plaintext, string KeyString, string NonceString, byte[] tag)
byte[] key = Convert.FromBase64String(KeyString);
byte[] nonce = Convert.FromBase64String(NonceString);
var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
var bcCiphertext = new byte[plaintextBytes.Length + tagLenth];
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), tagLenth * 8, nonce);
cipher.Init(true, parameters);
var offset = cipher.ProcessBytes(plaintextBytes, 0, plaintextBytes.Length, bcCiphertext, 0);
cipher.DoFinal(bcCiphertext, offset);
var ciphertext = new byte[plaintextBytes.Length];
Buffer.BlockCopy(bcCiphertext, 0, ciphertext, 0, plaintextBytes.Length);
Buffer.BlockCopy(bcCiphertext, plaintextBytes.Length, tag, 0, tagLenth);
return ciphertext;
and this is the decryption code.
private static string DecryptWithGCM(string EncryptedString, string KeyString, string NonceString, byte[] tag)
byte[] key = Convert.FromBase64String(KeyString);
byte[] nonce = Convert.FromBase64String(NonceString);
byte[] ciphertext = Convert.FromBase64String(EncryptedString);
var plaintextBytes = new byte[ciphertext.Length];
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), tag.Length * 8, nonce);
cipher.Init(false, parameters);
var bcCiphertext = ciphertext.Concat(tag).ToArray();
var offset = cipher.ProcessBytes(bcCiphertext, 0, bcCiphertext.Length, plaintextBytes, 0);
cipher.DoFinal(plaintextBytes, offset);
return Encoding.UTF8.GetString(plaintextBytes);
As you can see I am passing everything as string except the Tag
. because when I pass the Tag
as string and convert it to byte array it does not work. It shows error "Mac check in GCM failed"
So, this code works:
var rnd = new Random();
var tag = new Byte[16]; //16 bytes
rnd.NextBytes(tag);
string TagString = Convert.ToBase64String(tag);
byte[] EncryptedText = EncryptWithGCM(PlainText, KeyString, NonceString, tag);
string EncryptedString = Convert.ToBase64String(EncryptedText);
string DecryptdText = DecryptWithGCM(EncryptedString, KeyString, NonceString, tag);
But when I pass the TagString in the encryption/decryption functions and converting it back to byte array, it throws "Mac check in GCM failed"
error.
// this code does not work.
private static string DecryptWithGCM(string EncryptedString, string KeyString, string NonceString, string TagString)
byte[] key = Convert.FromBase64String(KeyString);
byte[] nonce = Convert.FromBase64String(NonceString);
byte[] tag = Convert.FromBase64String(TagString);
Why is this happening?
The tag is automatically created during encryption and used during decryption to authenticate the data (in both cases in DoFinal()
).
Since C#/BC automatically concatenates the tag with the ciphertext, the tag does not need to be passed explicitly during either encryption or decryption:
private static string EncryptWithGCM(string plaintext, string keyString, string nonceString)
var tagLength = 16;
var key = Convert.FromBase64String(keyString);
var nonce = Convert.FromBase64String(nonceString);
var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
var ciphertextTagBytes = new byte[plaintextBytes.Length + tagLength];
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), tagLength * 8, nonce);
cipher.Init(true, parameters);
var offset = cipher.ProcessBytes(plaintextBytes, 0, plaintextBytes.Length, ciphertextTagBytes, 0);
cipher.DoFinal(ciphertextTagBytes, offset); // create and append tag: ciphertext | tag
return Convert.ToBase64String(ciphertextTagBytes);
private static string DecryptWithGCM(string ciphertextTag, string keyString, string nonceString)
var tagLength = 16;
var key = Convert.FromBase64String(keyString);
var nonce = Convert.FromBase64String(nonceString);
var ciphertextTagBytes = Convert.FromBase64String(ciphertextTag);
var plaintextBytes = new byte[ciphertextTagBytes.Length - tagLength];
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), tagLength * 8, nonce);
cipher.Init(false, parameters);
var offset = cipher.ProcessBytes(ciphertextTagBytes, 0, ciphertextTagBytes.Length, plaintextBytes, 0);
cipher.DoFinal(plaintextBytes, offset); // authenticate data via tag
return Encoding.UTF8.GetString(plaintextBytes);
Note that with a fixed key, a static nonce is a fatal bug for GCM (here). The (non-secret) nonce should be randomly generated and passed to the decrypting side along with the ciphertext and tag (typically concatenated in the following order: nonce | ciphertext | tag).
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.