Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / security / encryption

Cross Platform AES 256 GCM Encryption / Decryption

5.00/5 (15 votes)
9 Jun 2023CPOL4 min read 88.3K  
Cross Platform AES 256 GCM Encryption and Decryption (C++, C# and Java)
The purpose of this article is to provide a starting point for projects which require strongest encryption on multiple platforms. Sample code is provided here and on GitHub repositories in C++ (Windows and Linux), Java and C#. Recently, a Linux based dynamic library and a test program explaining how to use it is uploaded on GitHub. C++ on Linux x64 means it could be used for NodeJS as well by just creating a wrapper. Similarly, it could be used on Apple machines easily as well.

Introduction

While working in security, identity management and data protection fields for a while, I found a very few working examples in the public domain on cross platform encryption based on AES 256 GCM algorithm. This is the same algorithm used by Google when you access Gmail, etc.

This article may help you implement very strong cross platform encryption / decryption. The sample code is in C++, C# and Java. However, Java through JNI (Java Native Interface) and C# through COM, can call native C++ code, which in my testing appears to be much faster as compared to pure Java or C# implementations. Still, there are times when one wants to do it without calling a native C++ layer.

For C#, to achieve AES 256 GCM encryption, I used Bouncy Castle cryptographic libraries. The code snippets available with this article work perfectly for encryption and decryption across various platforms. I tested it to be working on Linux (using Mono Framework) and Windows.

For C++ layer, I utilized Crypto++. This library is cross platform compatible (Windows, Linux and others like Solaris etc.). Crypto++ is a robust and very well implemented open source cryptographic library.

This article is not intended for beginners nor is it to teach AES GCM algorithm.

This article sort of provides you a sample code to implement with your own modifications.

C++ is a little complicated. Download Crypto++ source code. Create a console project and add existing Crypto++ project to solution. Then set your console project as startup project and set build dependency order.

Copy paste code from the article and correct header files paths (like pch.h) You can add source code directories in project properties as well to fix paths. In the same way, you can correct path to Crypto++ compiled library or add it to your project properties.

Another purpose of this article is to combine all three major programming languages sample code at one place. You don't have to search through thousands of individual samples, some of them do not work as intended. The code sample here works without any issue.

Recently, a Linux based dynamic library and a test program explaining how to use it is uploaded on GitHub. C++ on Linux x64 means it could be used for NodeJS as well by just creating a wrapper. Similarly, it could be used on Apple machines easily as well.

Background

Cross Platform AES 256 GCM Encryption and Decryption (C++, C# and Java)

You can also read more about Crypto++ AES GCM implementation or algorithm itself here and here.

Similarly, details about BouncyCastle can be found here.

BouncyCastle .NET used in C# code is here.

GitHub

Using the Code

For C#

Please add reference:

BouncyCastle.Crypto (BouncyCastle.Crypto.dll)

https://www.nuget.org/packages/BouncyCastle.Crypto.dll/

Image 1

C# Test Console Application

C#
using System;
namespace TestAES_GCM_256
{
    class Program
    {
        static void Main(string[] args)
        {
            //Generate and dump KEY so we could use again
            //Console.WriteLine(AesGcm256.toHex(AesGcm256.NewKey()));

            //Generate and dump IV so we could use again
            //Console.WriteLine(AesGcm256.toHex(AesGcm256.NewIv()));

            //Console.ReadKey();

            //using above code these key and iv was generated
            string hexKey = 
            "2192B39425BBD08B6E8E61C5D1F1BC9F428FC569FBC6F78C0BC48FCCDB0F42AE";
            string hexIV = "E1E592E87225847C11D948684F3B070D";

            string plainText = "Test encryption and decryption";
            Console.WriteLine("Plain Text: " + plainText);

            //encrypt - result base64 encoded string
            string encryptedText = AesGcm256.encrypt
                  (plainText, AesGcm256.HexToByte(hexKey), AesGcm256.HexToByte(hexIV));
            Console.WriteLine("Encrypted base64 encoded: " + encryptedText);

            //decrypt - result plain string
            string decryptedText = AesGcm256.decrypt
                  (encryptedText, AesGcm256.HexToByte(hexKey), 
                   AesGcm256.HexToByte(hexIV));
            Console.WriteLine("Decrypted Text: " + decryptedText);

            if (plainText.Equals(decryptedText))
            {
                Console.WriteLine("Test Passed");
            }
            else
            {
                Console.WriteLine("Test Failed");
            }

            /* Console Output
            Plain Text: Test encryption and decryption
            Encrypted base64 encoded: 
            A/boAixWJKflKviHp2cfDl6l/xn1qw2MsHcKFkrOfm2XOVmawIFct4fS1w7wKw==
            Decrypted Text: Test encryption and decryption
            Test Passed
            Press any key to continue . . .
            */
        }
    }
}

C# Encryption / Decryption Class

C#
using System;
using System.Text;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;

namespace TestAES_GCM_256
{
    public class AesGcm256
    {
        private static readonly SecureRandom Random = new SecureRandom();

        // Pre-configured Encryption Parameters
        public static readonly int NonceBitSize = 128;
        public static readonly int MacBitSize = 128;
        public static readonly int KeyBitSize = 256;

        private AesGcm256() { }

        public static byte[] NewKey()
        {
            var key = new byte[KeyBitSize / 8];
            Random.NextBytes(key);
            return key;
        }

        public static byte[] NewIv()
        {
            var iv = new byte[NonceBitSize / 8];
            Random.NextBytes(iv);
            return iv;
        }

        public static Byte[] HexToByte(string hexStr)
        {
            byte[] bArray = new byte[hexStr.Length / 2];
            for (int i = 0; i < (hexStr.Length / 2); i++)
            {
                byte firstNibble = Byte.Parse(hexStr.Substring((2 * i), 1), 
                      System.Globalization.NumberStyles.HexNumber); // [x,y)
                byte secondNibble = Byte.Parse(hexStr.Substring((2 * i) + 1, 1), 
                                    System.Globalization.NumberStyles.HexNumber);
                int finalByte = (secondNibble) | 
                                (firstNibble << 4); // bit-operations 
                                                    // only with numbers,
                                                    // not bytes.
                bArray[i] = (byte)finalByte;
            }
            return bArray;
        }

        public static string toHex(byte[] data)
        {
            string hex = string.Empty;
            foreach (byte c in data)
            {
                hex += c.ToString("X2");
            }
            return hex;
        }

        public static string toHex(string asciiString)
        {
            string hex = string.Empty;
            foreach (char c in asciiString)
            {
                int tmp = c;
                hex += string.Format
                       ("{0:x2}", System.Convert.ToUInt32(tmp.ToString()));
            }
            return hex;
        }

        public static string encrypt(string PlainText, byte[] key, byte[] iv)
        {
            string sR = string.Empty;
            try
            {
                byte[] plainBytes = Encoding.UTF8.GetBytes(PlainText);

                GcmBlockCipher cipher = new GcmBlockCipher(new AesFastEngine());
                AeadParameters parameters = 
                             new AeadParameters(new KeyParameter(key), 128, iv, null);

                cipher.Init(true, parameters);

                byte[] encryptedBytes = 
                       new byte[cipher.GetOutputSize(plainBytes.Length)];
                Int32 retLen = cipher.ProcessBytes
                               (plainBytes, 0, plainBytes.Length, encryptedBytes, 0);
                cipher.DoFinal(encryptedBytes, retLen);
                sR = Convert.ToBase64String
                     (encryptedBytes, Base64FormattingOptions.None);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            return sR;
        }

        public static string decrypt(string EncryptedText, byte[] key, byte[] iv)
        {
            string sR = string.Empty;
            try
            {
                byte[] encryptedBytes = Convert.FromBase64String(EncryptedText);

                GcmBlockCipher cipher = new GcmBlockCipher(new AesFastEngine());
                AeadParameters parameters = 
                          new AeadParameters(new KeyParameter(key), 128, iv, null);
                //ParametersWithIV parameters = 
                //new ParametersWithIV(new KeyParameter(key), iv);

                cipher.Init(false, parameters);
                byte[] plainBytes = 
                       new byte[cipher.GetOutputSize(encryptedBytes.Length)];
                Int32 retLen = cipher.ProcessBytes
                      (encryptedBytes, 0, encryptedBytes.Length, plainBytes, 0);
                cipher.DoFinal(plainBytes, retLen);

                sR = Encoding.UTF8.GetString(plainBytes).TrimEnd
                      ("\r\n\0".ToCharArray());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            return sR;
        }
    }
}  

For Java

Please add the following JAR to your Java project or class path:

bcprov-jdk15on-160.jar

Image 2

Java Test Console Application

Java
public class TestAes256GCM {

    public static void main(String[] args) {
            //Generate and dump KEY so we could use again
            //System.out.println(AesGcm256.toHex(AesGcm256.NewKey()));

            //Generate and dump IV so we could use again
            //System.out.println(AesGcm256.toHex(AesGcm256.NewIv()));

            //Console.ReadKey();

            //using above code these key and iv was generated
            String hexKey = 
            "2192B39425BBD08B6E8E61C5D1F1BC9F428FC569FBC6F78C0BC48FCCDB0F42AE";
            String hexIV = "E1E592E87225847C11D948684F3B070D";

            String plainText = "Test encryption and decryption";
            System.out.println("Plain Text: " + plainText);

            //encrypt - result base64 encoded string
            String encryptedText = AesGcm256.encrypt
              (plainText, AesGcm256.HexToByte(hexKey), AesGcm256.HexToByte(hexIV));
            System.out.println("Encrypted base64 encoded: " + encryptedText);

            //decrypt - result plain string
            String decryptedText = AesGcm256.decrypt
                    (encryptedText, AesGcm256.HexToByte(hexKey), 
                     AesGcm256.HexToByte(hexIV));
            System.out.println("Decrypted Text: " + decryptedText);

            if (plainText.equals(decryptedText))
            {
                System.out.println("Test Passed");
            }
            else
            {
                System.out.println("Test Failed");
            }

            /* Console Output
            Plain Text: Test encryption and decryption
            Encrypted base64 encoded: 
                   A/boAixWJKflKviHp2cfDl6l/xn1qw2MsHcKFkrOfm2XOVmawIFct4fS1w7wKw==
            Decrypted Text: Test encryption and decryption
            Test Passed
            Press any key to continue . . .
            */
    }    
}

Java Encryption / Decryption Class

Java
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.util.Base64;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.AESFastEngine;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;

public class AesGcm256 {

    private static final SecureRandom SECURE_RANDOM = new SecureRandom();

    // Pre-configured Encryption Parameters
    public static int NonceBitSize = 128;
    public static int MacBitSize = 128;
    public static int KeyBitSize = 256;

    private AesGcm256() {
    }

    public static byte[] NewKey() {
        byte[] key = new byte[KeyBitSize / 8];
        SECURE_RANDOM.nextBytes(key);
        return key;
    }

    public static byte[] NewIv() {
        byte[] iv = new byte[NonceBitSize / 8];
        SECURE_RANDOM.nextBytes(iv);
        return iv;
    }

    public static byte[] HexToByte(String hexStr) {
        int len = hexStr.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2)
        {
            data[i / 2] = (byte) ((Character.digit(hexStr.charAt(i), 16) << 4)
                    + Character.digit(hexStr.charAt(i + 1), 16));
        }
        return data;
    }

    public static String toHex(byte[] data) {
        final StringBuilder builder = new StringBuilder();
        for (byte b : data) {
            builder.append(Integer.toString(b, 16));
        }
        return builder.toString();
    }

    public static String encrypt(String PlainText, byte[] key, byte[] iv) {
        String sR = "";
        try {
            byte[] plainBytes = PlainText.getBytes("UTF-8");

            GCMBlockCipher cipher = new GCMBlockCipher(new AESFastEngine());
            AEADParameters parameters = 
                new AEADParameters(new KeyParameter(key), MacBitSize, iv, null);

            cipher.init(true, parameters);

            byte[] encryptedBytes = new byte[cipher.getOutputSize(plainBytes.length)];
            int retLen = cipher.processBytes
                         (plainBytes, 0, plainBytes.length, encryptedBytes, 0);
            cipher.doFinal(encryptedBytes, retLen);
            sR = Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (UnsupportedEncodingException | IllegalArgumentException | 
                 IllegalStateException | DataLengthException | 
                 InvalidCipherTextException ex) {
            System.out.println(ex.getMessage());
        }

        return sR;
    }

    public static String decrypt(String EncryptedText, byte[] key, byte[] iv) {
        String sR = "";
        try {
            byte[] encryptedBytes = Base64.getDecoder().decode(EncryptedText);

            GCMBlockCipher cipher = new GCMBlockCipher(new AESFastEngine());
            AEADParameters parameters = 
                     new AEADParameters(new KeyParameter(key), MacBitSize, iv, null);

            cipher.init(false, parameters);
            byte[] plainBytes = new byte[cipher.getOutputSize(encryptedBytes.length)];
            int retLen = cipher.processBytes
                         (encryptedBytes, 0, encryptedBytes.length, plainBytes, 0);
            cipher.doFinal(plainBytes, retLen);

            sR = new String(plainBytes, Charset.forName("UTF-8"));
        } catch (IllegalArgumentException | IllegalStateException | 
                         DataLengthException | InvalidCipherTextException ex) {
            System.out.println(ex.getMessage());
        }

        return sR;
    }
}

For C++ (Windows)

Please include Crypto++ Project in your test project.

Correct include and linked library paths:

Image 3

C++ Test Console Application

C++
// TestAES_GCM_256_C.cpp : Defines the entry point for the console application.
//

#pragma once
#include "stdafx.h"

#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE 1
#endif

#ifndef CRYPTOPP_DEFAULT_NO_DLL
#define CRYPTOPP_DEFAULT_NO_DLL 1
#endif

#ifndef CRYPTOPP_ENABLE_NAMESPACE_WEAK
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#endif

#ifdef _DEBUG

#ifndef x64
#pragma comment(lib, "./../../../common/Crypto5.6.1/Win32/Output/Debug/cryptlib.lib")
#else
#pragma comment(lib, "./../../../common/Crypto5.6.1/x64/Output/Debug/cryptlib.lib")
#endif
#else
#ifndef x64
#pragma comment(lib, "./../../../common/Crypto5.6.1/Win32/Output/Release/cryptlib.lib")
#else
#pragma comment(lib, "./../../../common/Crypto5.6.1/x64/Output/Release/cryptlib.lib")
#endif
#endif

// Crypto++ Include

#include "./../../../common/Crypto5.6.1/pch.h"
#include "./../../../common/Crypto5.6.1/files.h"
#include "./../../../common/Crypto5.6.1/default.h"
#include "./../../../common/Crypto5.6.1/base64.h"
#include "./../../../common/Crypto5.6.1/osrng.h"

//AES
#include "./../../../common/Crypto5.6.1/hex.h"
using CryptoPP::HexEncoder;
using CryptoPP::HexDecoder;

#include "./../../../common/Crypto5.6.1/cryptlib.h"
using CryptoPP::BufferedTransformation;
using CryptoPP::AuthenticatedSymmetricCipher;

#include "./../../../common/Crypto5.6.1/filters.h"
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::AuthenticatedEncryptionFilter;
using CryptoPP::AuthenticatedDecryptionFilter;

#include "./../../../common/Crypto5.6.1/aes.h"
using CryptoPP::AES;

#include "./../../../common/Crypto5.6.1/gcm.h"
using CryptoPP::GCM;
using CryptoPP::GCM_TablesOption;

#include <iostream>
#include <string>

USING_NAMESPACE(CryptoPP)
USING_NAMESPACE(std)

static inline RandomNumberGenerator& PSRNG(void);
static inline RandomNumberGenerator& PSRNG(void)
{
	static AutoSeededRandomPool rng;
	rng.Reseed();
	return rng;
}

bool encrypt_aes256_gcm(const char *aesKey, const char *aesIV, 
                        const char *inPlainText, char **outEncryptedBase64, 
                        int &dataLength);
bool decrypt_aes256_gcm(const char *aesKey, const char *aesIV, 
             const char *inBase64Text, char **outDecrypted, int &dataLength);
void Base64Decode(const std::string &inString, std::string &outString);
void HexDecode(const std::string &inString, std::string &outString);

static std::string m_ErrorMessage;

int main()
{
	//using above code these key and iv was generated
	std::string hexKey = 
         "2192B39425BBD08B6E8E61C5D1F1BC9F428FC569FBC6F78C0BC48FCCDB0F42AE";
	std::string hexDecodedKey;
	HexDecode(hexKey, hexDecodedKey);

	std::string hexIV = "E1E592E87225847C11D948684F3B070D";
	std::string hexDecodedIv;
	HexDecode(hexIV, hexDecodedIv);

	std::string plainText = "Test encryption and decryption";
	std::string encryptedWithJava = 
         "A/boAixWJKflKviHp2cfDl6l/xn1qw2MsHcKFkrOfm2XOVmawIFct4fS1w7wKw==";

	printf("%s%s\n", "Plain Text: " , plainText.c_str());

	//encrypt - result base64 encoded string
	char *outEncryptedText=NULL;
	int outDataLength=0;
	bool bR = encrypt_aes256_gcm(hexDecodedKey.c_str(), 
              hexDecodedIv.c_str(), plainText.c_str(), 
              &outEncryptedText, outDataLength);
	printf("%s%s\n", "Encrypted base64 encoded: " , outEncryptedText);

	//decrypt - result plain string
	char* outDecryptedText=NULL;
	int outDecryptedDataLength=0;
	bR = decrypt_aes256_gcm(hexDecodedKey.c_str(), 
         hexDecodedIv.c_str(), encryptedWithJava.c_str(), 
                               &outDecryptedText, outDecryptedDataLength);
	printf("%s%s\n", "Decrypted Text Encrypted by Java: " , outDecryptedText);

	if (plainText == outDecryptedText)
	{
		printf("%s\n", "Test Passed");
	}
	else
	{
		printf("%s\n", "Test Failed");
	}

	return 0;
}

bool encrypt_aes256_gcm(const char *aesKey, const char *aesIV, 
                        const char *inPlainText, 
                        char **outEncryptedBase64, int &dataLength)
{
	bool bR = false;
	//const int TAG_SIZE = 12;
	std::string outText;
	std::string outBase64;

	if (strlen(aesKey)>31 && strlen(aesIV)>15)
	{
		try
		{
			GCM< AES >::Encryption aesEncryption;
			aesEncryption.SetKeyWithIV(reinterpret_cast<const byte*>(aesKey), 
             AES::MAX_KEYLENGTH, reinterpret_cast<const byte*>(aesIV), AES::BLOCKSIZE);
			StringSource(inPlainText, true, new AuthenticatedEncryptionFilter
                        (aesEncryption, new StringSink(outText)
			) // AuthenticatedEncryptionFilter
			); // StringSource

			CryptoPP::Base64Encoder *base64Encoder = new CryptoPP::Base64Encoder
                                                     (new StringSink(outBase64), false);
			base64Encoder->PutMessageEnd(reinterpret_cast<const char="" 
                           unsigned=""> (outText.data()), outText.length());
			delete base64Encoder;

			dataLength = outBase64.length();
			if (outBase64.length()>0)
			{
				if (*outEncryptedBase64) free(*outEncryptedBase64);
				*outEncryptedBase64 = (char*)malloc(dataLength + 1);
				memset(*outEncryptedBase64, '\0', dataLength + 1);
				memcpy(*outEncryptedBase64, outBase64.c_str(), dataLength);

				bR = true;
			}
			else
			{
				m_ErrorMessage.append("Encryption Failed");
			}
		}
		catch (CryptoPP::InvalidArgument& e)
		{
			m_ErrorMessage.append(e.what());
		}
		catch (CryptoPP::Exception& e)
		{
			m_ErrorMessage.append(e.what());
		}
	}
	else
	{
		m_ErrorMessage.append("AES Key or IV cannot be empty");
	}

	outText.clear();
	outBase64.clear();

	return bR;
}

bool decrypt_aes256_gcm(const char *aesKey, const char *aesIV, 
                        const char *inBase64Text, char **outDecrypted, int &dataLength)
{
	bool bR = false;
	std::string outText;
	std::string pszDecodedText;
	Base64Decode(inBase64Text, pszDecodedText);

	if (strlen(aesKey)>31 && strlen(aesIV)>15)
	{
		try
		{
			GCM< AES >::Decryption aesDecryption;
			aesDecryption.SetKeyWithIV(reinterpret_cast<const byte*>(aesKey), 
                AES::MAX_KEYLENGTH, 
                reinterpret_cast<const byte*>(aesIV), AES::BLOCKSIZE);
			AuthenticatedDecryptionFilter df(aesDecryption, new StringSink(outText));

			StringSource(pszDecodedText, true,
				new Redirector(df /*, PASS_EVERYTHING */)
			); // StringSource

			bR = df.GetLastResult();

			dataLength = outText.length();
			if (outText.length()>0)
			{
				if (*outDecrypted) free(*outDecrypted);
				*outDecrypted = (char*)malloc(dataLength + 1);
				memset(*outDecrypted, '\0', dataLength + 1);
				memcpy(*outDecrypted, outText.c_str(), dataLength);

				bR = true;
			}
			else
			{
				m_ErrorMessage.append("Decryption Failed");
			}
		}
		catch (CryptoPP::HashVerificationFilter::HashVerificationFailed& e)
		{
			m_ErrorMessage.append(e.what());
		}
		catch (CryptoPP::InvalidArgument& e)
		{
			m_ErrorMessage.append(e.what());
		}
		catch (CryptoPP::Exception& e)
		{
			m_ErrorMessage.append(e.what());
		}
	}
	else
	{
		m_ErrorMessage.append("AES Key or IV cannot be empty");
	}

	return bR;
}

void Base64Decode(const std::string &inString, std::string &outString)
{
	StringSource(inString, true, new Base64Decoder(new StringSink(outString)));
}

void HexDecode(const std::string &inString, std::string &outString)
{
	StringSource(inString, true, new HexDecoder(new StringSink(outString)));
}

Linux Lib (GNU C++ 14 Standard)

This repository consists 2 Apache Netbeans projects here. Please clone the repository and open projects in Netbeans IDE. You must have already configured Netbeans IDE to be able to edit C++ projects.

Project in Netbeans IDE

Build Steps

Crypto++

To build, please open cryptopp870 folder in terminal and enter make command. It will build libcryptopp.a static library. The library defaults to OS architecture. GcmAes Netbeans project depends on libcryptopp.a and is linked to it.

Once the library is available, you can build both projects individually from Netbeans IDE. GcmAesTest project has libGcmAes.so dependency and will be pre-build. Once build, the projects could be debugged as well.

The test program validates few encryption and decryption tests. One of them is the decryption of text which was encrypted using Java code (links available below).

It produces the following sample test results:

Key=D303E8441BE72DD1501319D64A46CA6442031A29706000D2D3988175D7B492C4
Len=64

IV=D4AF4DB97D31492CA227D7BBB59AC494
Len=32
Key and IV Test OK

Test 1 -> Encrypted base64 encoded: 
          G3Elmv9n3cRyvVl5gZHtne9AEQHM3agHU1KTMvFxhSdWyASx5fD9uVfhVk+KRA==
Test 1 -> Decrypted: Test encryption and decryption
Test 1 -> Encryption / Decryption OK

Plain Text: Test encryption and decryption
Test 2 -> Encrypted base64 encoded: 
          A/boAixWJKflKviHp2cfDl6l/xn1qw2MsHcKFkrOfm2XOVmawIFct4fS1w7wKw==
Test 2 -> Decrypted: Test encryption and decryption
Test 2 -> Encryption / Decryption OK

Test 3 -> Decrypted: Test encryption and decryption
Test 3 -> Java Encrypted / C++ Decryption OK

Test 4 -> Multi-byte Text: syllabic kana – hiragana (平仮名) and katakana (片仮名)
Test 4 -> Hex Encoded: 
          73796C6C61626963206B616E6120E28093206869726167616E612028E5B9B3E4BBAEE5908D2920616E64206B6174616B616E612028E78987E4BBAEE5908D29
Test 4 -> Hex Encoding OK

Test 5 -> Multi-byte Text: syllabic kana – hiragana (平仮名) and katakana (片仮名)
Test 5 -> Hex Decoded: syllabic kana – hiragana (平仮名) and katakana (片仮名)
Test 5 -> Hex Decoding OK

Test 6 -> Multi-byte Text: syllabic kana – hiragana (平仮名) and katakana (片仮名)
Test 6 -> Base64 Encoded: 
          c3lsbGFiaWMga2FuYSDigJMgaGlyYWdhbmEgKOW5s+S7ruWQjSkgYW5kIGthdGFrYW5hICjniYfku67lkI0p
Test 6 -> Base64 Encoding OK

Test 7 -> Multi-byte Text: syllabic kana – hiragana (平仮名) and katakana (片仮名)
Test 7 -> Base64 Decoded: syllabic kana – hiragana (平仮名) and katakana (片仮名)
Test 7 -> Base64 Decoding OK

The test project also demonstrates how to use the libGcmAes.so with other C++ projects on Linux like systems. Normally, just have to include GcmAes.h in your project and must link your project against libGcmAes.so. Code uses C++ 14 standard.

GcmAes.h

C++
/*
Copyright (©) 2023 Kashif Mushtaq

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
 */

#ifndef __CRYPTOGRAPHER_INCLUDE_HEADER__
#define __CRYPTOGRAPHER_INCLUDE_HEADER__

#pragma once
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <syslog.h>
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

/**
Exported for dynamic loading and calling
 */
#ifdef __cplusplus
extern "C" {
#endif

    /*
     * Note:
     * All functions returning buffers like **out<name> 
     * are allocated using new and are null terminated.
     * Users must clear them using delete *out<name>
     */

    // ------------------------------ libGcmAes.so Exports ---------------------------
    bool _base64Encode(/*[in]*/ const char *inPlainText, 
         /*[out]*/ char **outBase64Encoded, /*[in, out]*/ int &dataLength);
    bool _base64Decode(/*[in]*/ const char *inBase64Text, 
         /*[out]*/ char **outPlainText, /*[in, out]*/ int &dataLength);
    bool _hexDecode(/*[in]*/ const char *inHexEncodedText, 
         /*[out]*/ char **outHexDecoded);
    bool _hexEncode(/*[in]*/ const char *inData, /*[out]*/ char **outHexEncoded);

    bool _encrypt_GcmAes256(/*[in]*/const char *inHexKey, 
         /*[in]*/const char *inHexIv, /*[in]*/const char *inPlainText, 
         /*[out]*/ char **outEncryptedBase64, /*[in, out]*/int &dataLength);
    bool _decrypt_GcmAes256(/*[in]*/const char *inHexKey, 
         /*[in]*/const char *inHexIv, /*[in]*/const char *inBase64Text, 
         /*[out]*/ char **outDecrypted, /*[in, out]*/int &dataLength);

    bool _getNewAESKeyAndIv(/*[out]*/ char **outHexKey, 
         /*[out]*/ char **outHexIv, /*[in, out]*/int &outKeyLength, 
         /*[in, out]*/ int &outIvLength);
    // ------------------------------ libGcmAes.so Exports ---------------------------

#ifdef __cplusplus
}
#endif

#endif

Test Program

C++
/*
Copyright (©) 2023 Kashif Mushtaq

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
 */

/* 
 * File:   main.cpp
 * Author: kmushtaq
 *
 * Created on April 11, 2023, 9:21 AM
 */

//Include GcmAES header and link this project against libGcmAes.so
#include "GcmAes.h"

#include <cstdlib>
#include <regex>
#include <cstring>

using namespace std;

/*
 * 
 */
int main(int argc, char** argv)
{
    // lets first generate encryption Key and IV for AES 256 GCM. 
    // Key size is 32 bytes and IV size is 16 bytes
    // Lib generated Key and IV are hex encoded. 
    // Encryption and decryption function expects hex encoded Key and IV.

    std::string inHexKey;
    std::string inHexIV;
    char* pszKey = nullptr;
    char* pszIv = nullptr;
    int keyLen = 0;
    int ivLen = 0;

    std::string plainText = "Test encryption and decryption";
    bool result = false;

    /* ----------------------- Key Generation Test ----------------------- */
    result = _getNewAESKeyAndIv(&pszKey, &pszIv, keyLen, ivLen);

    if (result)
    {
        printf("Key=%s\nLen=%i\n\nIV=%s\nLen=%i\n", pszKey, keyLen, pszIv, ivLen);
        printf("Key and IV Test OK\n\n");
    }
    else
    {
        printf("%s", "Key and IV generation failed. Please check syslog for errors");
        return 1;
    }
    /* ----------------------- Key Generation Test ----------------------- */

    /* ----------------------- Encryption Decryption Test ----------------------- */
    //Save key and IV to call encryption and decryption functions
    inHexKey = pszKey;
    inHexIV = pszIv;

    char* outTestEncrypted = nullptr;
    int outTestEncryptedLen = 0;

    //encrypt - result base64 encoded
    result = _encrypt_GcmAes256(inHexKey.c_str(), inHexIV.c_str(), 
             plainText.c_str(), &outTestEncrypted, outTestEncryptedLen);
    if (result)
    {
        printf("Test 1 -> Encrypted base64 encoded: %s\n", outTestEncrypted);
    }
    else
    {
        printf("%s", "Test 1 -> Encryption failed. Please check syslog for errors");
        return 1;
    }

    char* outTestDecrypted = nullptr;
    int outTestDecryptedLen = 0;

    //decrypt - result plain text
    result = _decrypt_GcmAes256(inHexKey.c_str(), inHexIV.c_str(), 
             outTestEncrypted, &outTestDecrypted, outTestDecryptedLen);
    if (result && strcmp(plainText.c_str(), outTestDecrypted) == 0)
    {
        printf("Test 1 -> Decrypted: %s\n", outTestDecrypted);
        printf("Test 1 -> Encryption / Decryption OK\n\n");
    }
    else
    {
        printf("%s", "Test 1 -> Decryption failed. Please check syslog for errors");
        return 1;
    }

    inHexKey.clear();
    inHexIV.clear();

    //clear buffers
    if (outTestEncrypted) delete outTestEncrypted;
    outTestEncrypted = nullptr;

    if (outTestDecrypted) delete outTestDecrypted;
    outTestDecrypted = nullptr;


    //clear buffers allocated by _getNewAESKeyAndIv function
    if (pszKey) delete pszKey;
    pszKey = nullptr;

    if (pszIv) delete pszIv;
    pszIv = nullptr;

    /* ------------------ Encryption Decryption Test ----------------------- */

    /* ------------------ C++ Encryption and C++ Decryption Test ------------------ */
    std::string hexKey = 
         "2192B39425BBD08B6E8E61C5D1F1BC9F428FC569FBC6F78C0BC48FCCDB0F42AE";
    std::string hexIV = "E1E592E87225847C11D948684F3B070D";

    printf("Plain Text: %s\n", plainText.c_str());

    char* outEncrypted = nullptr;
    int outEncryptedLen = 0;

    //encrypt - result base64 encoded
    result = _encrypt_GcmAes256(hexKey.c_str(), hexIV.c_str(), 
             plainText.c_str(), &outEncrypted, outEncryptedLen);
    if (result)
    {
        printf("Test 2 -> Encrypted base64 encoded: %s\n", outEncrypted);
    }
    else
    {
        printf("%s", "Test 2 -> Encryption failed. Please check syslog for errors");
        return 1;
    }

    char* outDecrypted = nullptr;
    int outDecryptedLen = 0;

    //decrypt - result plain text
    result = _decrypt_GcmAes256(hexKey.c_str(), 
             hexIV.c_str(), outEncrypted, &outDecrypted, outDecryptedLen);
    if (result && strcmp(plainText.c_str(), outDecrypted) == 0)
    {
        printf("Test 2 -> Decrypted: %s\n", outDecrypted);
        printf("Test 2 -> Encryption / Decryption OK\n\n");
    }
    else
    {
        printf("%s", "Test 2 -> Decryption failed. Please check syslog for errors");
        return 1;
    }

    //clear buffers
    if (outEncrypted) delete outEncrypted;
    outEncrypted = nullptr;

    if (outDecrypted) delete outDecrypted;
    outDecrypted = nullptr;

    /* ------------------ C++ Encryption and C++ Decryption Test ------------------ */

    /* ------------------ Java based Encryption and C++ Decryption Test ----------- */
    //Java Encrypted with same Key and IV as above
    // A/boAixWJKflKviHp2cfDl6l/xn1qw2MsHcKFkrOfm2XOVmawIFct4fS1w7wKw==

    std::string javaEncrypted = 
         "A/boAixWJKflKviHp2cfDl6l/xn1qw2MsHcKFkrOfm2XOVmawIFct4fS1w7wKw==";
    char* outCDecrypted;
    int outCDecryptedLen = 0;

    //decrypt - result plain text
    result = _decrypt_GcmAes256(hexKey.c_str(), hexIV.c_str(), 
             javaEncrypted.c_str(), &outCDecrypted, outCDecryptedLen);
    if (result && strcmp(plainText.c_str(), outCDecrypted) == 0)
    {
        printf("Test 3 -> Decrypted: %s\n", outCDecrypted);
        printf("Test 3 -> Java Encrypted / C++ Decryption OK\n\n");
    }
    else
    {
        printf("%s", "Test 3 -> Java Decryption failed. 
                Please check syslog for errors");
        return 1;
    }

    //clear buffers
    if (outCDecrypted) delete outCDecrypted;
    outCDecrypted = nullptr;

    /* ---------------- Java based Encryption and C++ Decryption Test --------------- */

    /*
    Result:
    
    Key=BF2F0CEC3D78EEB8388D8A78402510B9F3A99F7F69E98C6DB7168D0C57270EF4
    Len=64

    IV=FE6A26A45DB769F02C55CF12252F8A32
    Len=32
    Key and IV Test OK

    Test 1 -> Encrypted base64 encoded: 
              5NKdId7ZkBMWTtM5OhtPiaq1x0lBhbUSCxpcWK8PjjIGUvVdMdYET0Gw1mwmOw==
    Test 1 -> Decrypted: Test encryption and decryption
    Test 1 -> Encryption / Decryption OK

    Plain Text: Test encryption and decryptionTest 2 -> 
    Encrypted base64 encoded: A/boAixWJKflKviHp2cfDl6l/xn1qw2MsHcKFkrOfm2XOVmawIFct4fS1w7wKw==
    Test 2 -> Decrypted: Test encryption and decryption
    Test 2 -> Encryption / Decryption OK

    Test 3 -> Decrypted: Test encryption and decryption
    Test 3 -> Java Encrypted / C++ Decryption OK


    RUN FINISHED; exit value 0; real time: 20ms; user: 0ms; system: 0ms

     */
    
    /* ----------------------- Hex Encoding / Decoding Test ----------------------- */
    
    std::string pszPlainText = "syllabic kana – hiragana (平仮名) and katakana (片仮名)";
    char* hexEncoded = nullptr;
    char* hexDecoded = nullptr;
    
    result = _hexEncode(pszPlainText.c_str(), &hexEncoded);
    
    if(result)
    {
        printf("Test 4 -> Multi-byte Text: %s\n", pszPlainText.c_str());
        printf("Test 4 -> Hex Encoded: %s\n", hexEncoded);
        printf("Test 4 -> Hex Encoding OK\n\n");
    }
    else
    {
        printf("%s", "Test 4 -> Encoding failed.");
        return 1;
    }
    
    result = _hexDecode(hexEncoded, &hexDecoded);
    
    if(result && strcmp(pszPlainText.c_str(), hexDecoded) == 0)
    {
        printf("Test 5 -> Multi-byte Text: %s\n", pszPlainText.c_str());
        printf("Test 5 -> Hex Decoded: %s\n", hexDecoded);
        printf("Test 5 -> Hex Decoding OK\n\n");
    }
    else
    {
        printf("%s", "Test 4 -> Decoding failed.");
        return 1;
    }
    
    if(hexEncoded) delete hexEncoded;
    if(hexDecoded) delete hexDecoded;
    hexEncoded = nullptr;
    hexDecoded = nullptr;

    /* ----------------------- Hex Encoding / Decoding Test ----------------------- */
    
    
    /* ----------------------- Base64 Encoding / Decoding Test -------------------- */
    
    char* base64Encoded = nullptr;
    char* base64Decoded = nullptr;
    int base64Len = pszPlainText.length();
    
    result = _base64Encode(pszPlainText.c_str(), &base64Encoded, base64Len);
    
    if(result)
    {
        printf("Test 6 -> Multi-byte Text: %s\n", pszPlainText.c_str());
        printf("Test 6 -> Base64 Encoded: %s\n", base64Encoded);
        printf("Test 6 -> Base64 Encoding OK\n\n");
    }
    else
    {
        printf("%s", "Test 6 -> Encoding failed.");
        return 1;
    }
    
    base64Len = strlen(base64Encoded);
    result = _base64Decode(base64Encoded, &base64Decoded, base64Len);
    
    if(result && strcmp(pszPlainText.c_str(), base64Decoded) == 0)
    {
        printf("Test 7 -> Multi-byte Text: %s\n", pszPlainText.c_str());
        printf("Test 7 -> Base64 Decoded: %s\n", base64Decoded);
        printf("Test 7 -> Base64 Decoding OK\n\n");
    }
    else
    {
        printf("%s", "Test 7 -> Decoding failed.");
        return 1;
    }
    
    if(base64Encoded) delete base64Encoded;
    if(base64Decoded) delete base64Decoded;
    base64Encoded = nullptr;
    base64Decoded = nullptr;
    
    /* ----------------------- Base64 Encoding / Decoding Test -------------------- */
    
    hexKey.clear();
    hexIV.clear();
    pszPlainText.clear();
    
    return 0;
}

Windows x64 C++ DLL

Project covers exactly all the functionality and test and usage cases as Linux lib project above.
Please visit Windows C++ x64 DLL and Example Test Program at GitHub.

Node and C# (.Net Core) Wrappers using libGcmAes.so

Two projects provide wrapper classes and test projects to call libGcmAes.so exported functions related to encryption and decryption. A Docker container (Linux x64) can use these wrappers to implement AES 256 GCM cross platform encryption and decryption.
Please visit NodeJS and .Net Core Wrappers at GitHub.

History

  • 29th October, 2018: First published
  • 12th April, 2023: Linux Lib source published
  • 13th April, 2023: Hex and Base64 encoding and decoding exposed. More test cases added
  • 15th April, 2023: Windows x64 C++ DLL GitHub project link added. It covers exactly Linux lib functionality and test cases
  • 3rd June, 2023: NodeJS and .Net Core GitHub projects link added.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)