I'm trying to de/encode a message using AES-256-GCM with several hashes based on a PHP snippet

244 Views Asked by At

I've been breaking my head over this for a few days. To make an API call I have to encrypt some data. The receiving people gave me this code snippet to use for the encryption.

<?php
# Config
$algo = 'AES-256-GCM';
$hmac = 'SHA3-256';
$key = 'PUBLIC-KEY-HERE';
$encoded_key = hash_pbkdf2($hmac, substr($key, 0, 16), substr($key, 16), 4096, 32, TRUE);
$tagLength = 16;
$checksumlength = 64;
# Decrypt
$ciphertext = base64_decode("4T9Rm2FSRUrfkVH58Vd+oLCvwqOtVRKyHz8pDGMzM2IyZTNkZWE1NmFlNjQxMDgyNzZhYjIyOWNhZWI1YmU5OTc4N2U0OWVkYjY3ZjQ1NjZmYTI3NDQ3MzY3NzYAEHU4a3+czd8YSQ==");
$ivLength = openssl_cipher_iv_length('$AES-256-GCM');
$iv = substr($ciphertext, 0, $ivLength);
$tag = substr($ciphertext, $ivLength, $tagLength);
$checksum = substr($ciphertext, $ivLength + $tagLength, $checksumlength);
$payload = substr($ciphertext, $ivLength + $tagLength + $checksumlength);
try {
    $result = openssl_decrypt($payload, $algo, $encoded_key, OPENSSL_RAW_DATA, $iv, $tag);
}
catch(Exception $e) {
    echo $e->getMessage();
}
var_dump($result);
if(hash_hmac($hmac, $result, $key) == $checksum) {
    echo "<p>Valid decryption</p>";
}
# Encrypt
$payload = 'Lorum Ipsum';
$iv = openssl_random_pseudo_bytes($ivLength);
$checksum = hash_hmac($hmac, $payload, $key);
$tag= null;
$encrypted_text = openssl_encrypt($payload, $algo, $encoded_key, OPENSSL_RAW_DATA, $iv, $tag, '', $tagLength);
$result = base64_encode($iv . $tag . $checksum . $encrypted_text);
var_dump($result);

This is what I've made

        public static string EncryptUsingAes(string plainText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException(nameof(plainText));
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException(nameof(Key));
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException(nameof(IV));
            

            // with the specified key and IV.
            var keyParam = new KeyParameter(Key);
            var parameters = new ParametersWithIV(keyParam, IV);

            var cipher = new GcmBlockCipher(new AesEngine());
            cipher.Init(true, parameters);

            var plainTextBytes = Encoding.UTF8.GetBytes(plainText);

            var ciphertext = new byte[cipher.GetOutputSize(plainTextBytes.Length)];
            var len = cipher.ProcessBytes(plainTextBytes, 0, plainTextBytes.Length, ciphertext, 0);
            cipher.DoFinal(ciphertext, len);

            var tag = cipher.GetMac();
            var checksum = GeneratePBKDF2Key(Encoding.UTF8.GetString(Key), plaintext);
            var combinedBytes = CombineByteArrays(IV, tag, checksum, ciphertext);
            var result = Convert.ToBase64String(combinedBytes);
            
            return result;
        }

        public static string DecryptUsingAes(string encrypted, byte[] Key)
        {
            var decoded = Convert.FromBase64String(encrypted);
            const int ivLength = 12;
            const int tagLength = 16;
            const int checksumLength = 32; //64 bytes
            var iv = new byte[ivLength];
            var tag = new byte[tagLength];
            var checksum = new byte[checksumLength];
            var encryptedText = new byte[decoded.Length - ivLength - tagLength - checksumLength];

            Buffer.BlockCopy(decoded, 0, iv, 0, ivLength);
            Buffer.BlockCopy(decoded, ivLength, tag, 0, tagLength);
            Buffer.BlockCopy(decoded, ivLength + tagLength, checksum, 0, checksumLength);
            Buffer.BlockCopy(decoded, ivLength + tagLength + checksumLength, encryptedText, 0, encryptedText.Length);


            var keyParam = new KeyParameter(Key);
            var parameters = new ParametersWithIV(keyParam, iv);

            var cipher = new GcmBlockCipher(new AesEngine());
            cipher.Init(false, parameters);
            
            var plaintextBytes = new byte[cipher.GetOutputSize(encryptedText.Length)];
            var len = cipher.ProcessBytes(encryptedText, 0, encryptedText.Length, plaintextBytes, 0);
            cipher.DoFinal(plaintextBytes, len);

            var checksum2 = GeneratePBKDF2Key(Encoding.UTF8.GetString(Key), Encoding.UTF8.GetString(plaintextBytes));

            Console.WriteLine(checksum2.SequenceEqual(checksum)
                ? "The encrypted data is valid."
                : "The encrypted data is invalid.");

            return Encoding.UTF8.GetString(plaintextBytes);
        }

        public static byte[] CombineByteArrays(params byte[][] arrays)
        {
            var totalLength = 0;
            foreach (var array in arrays)
            {
                totalLength += array.Length;
            }

            var combinedArray = new byte[totalLength];
            var offset = 0;
            foreach (var array in arrays)
            {
                Buffer.BlockCopy(array, 0, combinedArray, offset, array.Length);
                offset += array.Length;
            }

            return combinedArray;
        }

        public static byte[] getIv()
        {
            var random = new SecureRandom();
            var iv = new byte[12];
            random.NextBytes(iv);
            return iv;
        }

        public static byte[] GeneratePBKDF2Key(string key, string plaintext)
        {
            var keyBytes = Encoding.UTF8.GetBytes(key);
            var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
            IDigest digest = new Sha3Digest(256);
            var keyParameter1 = new KeyParameter(keyBytes);
            var hmac = new HMac(digest);
            hmac.Init(keyParameter1);

            var checksum = new byte[hmac.GetMacSize()];
            hmac.BlockUpdate(plaintextBytes, 0, plaintextBytes.Length);
            hmac.DoFinal(checksum, 0);
            return checksum;
        }

I have to use bouncycastle given the .NET version we are using.

I am able te encrypt and decrypt the message. But I get a "Cannot decrypt message" error when I do the API call.

I've searched the documentation of boucycastle, checked Stack Overflow, I've even asked ChatGPT to help with the code, but I can't seem to get it to work.

EDIT after some searching and trying I have come to this, it uses the PBKDF2 correctly now and it seems the tag is appended on the ciphertext, so now i pop it off en set it elsewhere.

        public static string EncryptUsingAes(string plainText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException(nameof(plainText));
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException(nameof(Key));
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException(nameof(IV));
            

            // with the specified key and IV.
            var unused = Convert.ToBase64String(Key);
            var hashedKey = GeneratePBKDF2Hash(Key);
            var keyParam = new KeyParameter(hashedKey);
            var parameters = new AeadParameters(keyParam, 128, IV);
            var hasedKeyString = Convert.ToBase64String(hashedKey);

            var cipher = new GcmBlockCipher(new AesEngine());
            cipher.Init(true, parameters);

            var IVstring = Convert.ToBase64String(IV);

            var plainTextBytes = Encoding.UTF8.GetBytes(plainText);

            var ciphertextTag = new byte[cipher.GetOutputSize(plainTextBytes.Length)];
            var len = cipher.ProcessBytes(plainTextBytes, 0, plainTextBytes.Length, ciphertextTag, 0);
            cipher.DoFinal(ciphertextTag, len);

            var tag = ciphertextTag.Skip(ciphertextTag.Length - 16).Take(16).ToArray();
            var ciphertext = ciphertextTag.Take(ciphertextTag.Length - 16).ToArray();
            var TESTTAG = cipher.GetMac();
            var checksum = GenerateHmacSha3_256(Key, ciphertext);
            var combinedBytes = CombineByteArrays(IV, tag, checksum, ciphertext);
            
            var result = Convert.ToBase64String(combinedBytes);
            
            return result;
        }

        public static string DecryptUsingAes(string encrypted, byte[] Key)
        {
            var decoded = Convert.FromBase64String(encrypted);
            const int ivLength = 12;
            const int tagLength = 16;
            const int checksumLength = 64; 
            var iv = new byte[ivLength];
            var tag = new byte[tagLength];
            var checksum = new byte[checksumLength];
            var encryptedText = new byte[decoded.Length - ivLength - tagLength - checksumLength];

            Buffer.BlockCopy(decoded, 0, iv, 0, ivLength);
            Buffer.BlockCopy(decoded, ivLength, tag, 0, tagLength);
            Buffer.BlockCopy(decoded, ivLength + tagLength, checksum, 0, checksumLength);
            Buffer.BlockCopy(decoded, ivLength + tagLength + checksumLength, encryptedText, 0, encryptedText.Length);

            var ciphertext = Convert.ToBase64String(encryptedText);
            var ivtext = Convert.ToBase64String(iv);

            //var generator = GeneratorUtilities.GetKeyGenerator("AES");
            //generator.Init(new KeyGenerationParameters(new SecureRandom(), 256));
            var hashedkey = GeneratePBKDF2Hash(Key);
            var keyParam = new KeyParameter(hashedkey);
            var parameters = new AeadParameters(keyParam, 128, iv);
            var hashedKeyString = Convert.ToBase64String(hashedkey);

                var cipher = new GcmBlockCipher(new AesEngine());
            cipher.Init(false, parameters);

            var encryptedTextTag = CombineByteArrays(encryptedText, tag);
            var plaintextBytes = new byte[cipher.GetOutputSize(encryptedTextTag.Length)];
            var len = cipher.ProcessBytes(encryptedTextTag, 0, encryptedTextTag.Length, plaintextBytes, 0);
            cipher.DoFinal(plaintextBytes, len);

            var checksum2 = GenerateHmacSha3_256(Key, encryptedText);

            Console.WriteLine(checksum2.SequenceEqual(checksum)
                ? "The encrypted data is valid."
                : "The encrypted data is invalid.");

            return Encoding.UTF8.GetString(plaintextBytes);
        }

        public static byte[] CombineByteArrays(params byte[][] arrays)
        {
            var totalLength = 0;
            foreach (var array in arrays)
            {
                totalLength += array.Length;
            }

            var combinedArray = new byte[totalLength];
            var offset = 0;
            foreach (var array in arrays)
            {
                Buffer.BlockCopy(array, 0, combinedArray, offset, array.Length);
                offset += array.Length;
            }

            return combinedArray;
        }

        public static byte[] getIv()
        {
            var random = new SecureRandom();
            var iv = new byte[12];
            random.NextBytes(iv);
            return iv;
        }


    

        public static byte[] GeneratePBKDF2Hash(byte[] key)
        {
            int iterationCount = 4096;
            int keySize = 32;
            string hmacAlgorithm = "SHA3-256";

            // Split the key into two parts: first 16 bytes and the remaining bytes
            var keyPart1 = new byte[16];
            var keyPart2 = new byte[key.Length - 16];
            Buffer.BlockCopy(key, 0, keyPart1, 0, 16);
            Buffer.BlockCopy(key, 16, keyPart2, 0, key.Length - 16);

            // Create a digest based on the specified HMAC algorithm
            IDigest digest;
            if (hmacAlgorithm.Equals("SHA3-256", StringComparison.OrdinalIgnoreCase))
                digest = new Sha3Digest(256);
            else
                throw new NotSupportedException("Unsupported HMAC algorithm.");

            // Generate the PBKDF2 parameters
            var generator = new Pkcs5S2ParametersGenerator(digest);
            generator.Init(keyPart1, keyPart2, iterationCount);

            // Generate the derived key
            var keyParameter = (KeyParameter)generator.GenerateDerivedParameters(keySize * 8);
             
            var unused = Convert.ToBase64String(keyParameter.GetKey());

            return keyParameter.GetKey();
        }

        public static byte[] GenerateHmacSha3_256(byte[] key, byte[] payload)
        {
            // Create a SHA3-256 digest
            IDigest digest = new Sha3Digest(256);

            // Create an HMAC generator with the digest and key
            var hmac = new HMac(digest);
            hmac.Init(new KeyParameter(key));

            // Compute the HMAC
            hmac.BlockUpdate(payload, 0, payload.Length);
            var hmacBytes = new byte[hmac.GetMacSize()];
            hmac.DoFinal(hmacBytes, 0);

            var unused = Convert.ToBase64String(hmacBytes);

            return hmacBytes;
        }
0

There are 0 best solutions below