相关文章推荐
不开心的夕阳  ·  使用 Swift ...·  2 天前    · 
知识渊博的热带鱼  ·  这才是 SpringBoot ...·  22 小时前    · 
不拘小节的爆米花  ·  JavaScriptSerializer.D ...·  3 小时前    · 
鼻子大的煎饼果子  ·  Android ...·  7 月前    · 
腼腆的水桶  ·  Errno 9: Bad file ...·  9 月前    · 
个性的钱包  ·  spring data jpa - ...·  1 年前    · 
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.