I've got a string that was AES-GCM encrypted in Go, along with it's pass phrase and am trying to decrypt it in C#. However, I'm unable to find the right methods to decrypt the same in C#. The errors I'm getting mentions the size of the IV, block not being the right lengths for the C# decryption algorithm. Below are the values from Go:
AES encr/decr Passphrase: this-is-a-test-passphrase
Input String: text to encrypt hello world 123
Encrypted String: 94b681ef29d9a6d7-e6fa36c4c00977de1745fc63-a1ad0481bdbeeaa02c013a2dce82520ddd762355e18f1e2f20c0ea9d001ece24e9b8216ed4b9c6a06e1ef34c953f80
GO code: https://go.dev/play/p/jN8Ie61Ntzw
This is the decryption code in Go
func AppDecryptImpl(passphrase, ciphertext string) string {
arr := strings.Split(ciphertext, "-")
salt, _ := hex.DecodeString(arr[0])
iv, _ := hex.DecodeString(arr[1])
data, _ := hex.DecodeString(arr[2])
key, _ := appDeriveKey(passphrase, salt)
b, _ := aes.NewCipher(key)
aesgcm, _ := cipher.NewGCM(b)
data, _ = aesgcm.Open(nil, iv, data, nil)
return string(data)
}
func appDeriveKey(passphrase string, salt []byte) ([]byte, []byte) {
if salt == nil {
salt = make([]byte, 8)
rand.Read(salt)
}
return pbkdf2.Key([]byte(passphrase), salt, 1000, 32, sha256.New), salt
}
And this is the encryption code in Go
func AppEncryptImpl(passphrase string, plaintext string) string {
key, salt := appDeriveKey(passphrase, nil)
iv := make([]byte, 12)
rand.Read(iv)
b, _ := aes.NewCipher(key)
aesgcm, _ := cipher.NewGCM(b)
data := aesgcm.Seal(nil, iv, []byte(plaintext), nil)
return hex.EncodeToString(salt) "-" hex.EncodeToString(iv) "-" hex.EncodeToString(data)
}
I'm trying to replicate the same decryption login in C#, so it'll be able to decrypt and produce the final string.
I've tried several logic when it comes to decryption in C#, they can be found here:
https://dotnetfiddle.net/32sb5m This one uses the System.Security.Cryptography namespace, but it results in an IV size error.
https://dotnetfiddle.net/WxKUYR A modified version of the above for .NET 5 produces the same result
https://dotnetfiddle.net/6IfTps Using the bouncy castle library results in an "mac check in GCM failed" error
https://dotnetfiddle.net/8mJS3G Another method using the Rfc2898DeriveBytes method produces an error saying "The computed authentication tag did not match the input authentication tag"
Is the approach that's currently being used the right approach or is there another method to decrypt AES-GCM it in C#? And what can be done to bypass these errors when it comes to C#?
CodePudding user response:
You are close with the last code. Go appends authentication tag to the end of produced ciphertext. And you correctly extract it here:
// Extract the tag from the encrypted byte array
byte[] tag = new byte[16];
Array.Copy(encryptedData, encryptedData.Length - 16, tag, 0, 16);
But then you continue to treat array with actual encrypted text auth tag as if it contained only encrypted text. To fix, extract it also:
public static void Main() {
// This is the encrypted string that you provided
string encryptedString = "a6c0952b78967559-2953e738b9b005028bf4f6c0-7b8464d1ed75bc38b4503f6c8d25d6bfc22a19cc1a8a92bc6faa1ed6cd837b97072bc8e16fd95b6cfca67fccbad8fc";
// This is the passphrase that you provided
string passphrase = "this-is-a-test-passphrase";
string[] splitStrs = encryptedString.Split('-');
byte[] salt = Convert.FromHexString(splitStrs[0]);
byte[] iv = Convert.FromHexString(splitStrs[1]);
// this is encrypted data tag
byte[] encryptedDataWithTag = Convert.FromHexString(splitStrs[2]);
// Extract the tag from the encrypted byte array
byte[] tag = new byte[16];
// But also extract actual encrypted data
byte[] encryptedData = new byte[encryptedDataWithTag.Length - 16];
Array.Copy(encryptedDataWithTag, 0, encryptedData, 0, encryptedData.Length);
Array.Copy(encryptedDataWithTag, encryptedDataWithTag.Length - 16, tag, 0, 16);
byte[] key = new Rfc2898DeriveBytes(passphrase, salt, 1000, HashAlgorithmName.SHA256).GetBytes(32);
// Create an AesGcm object
AesGcm aesGcm = new AesGcm(key);
int textLength = encryptedData.Length;
// Decrypt the ciphertext
byte[] plaintext = new byte[textLength];
aesGcm.Decrypt(iv, encryptedData, tag, plaintext);
// Convert the plaintext to a string and print it
string decryptedString = Encoding.UTF8.GetString(plaintext);
Console.WriteLine(decryptedString);
}