跨平台 AES 256 GCM 加密/解密





5.00/5 (15投票s)
跨平台 AES 256 GCM 加密和解密 (C++, C# 和 Java)
引言
在安全、身份管理和数据保护领域工作一段时间后,我发现公共领域中基于 AES 256 GCM 算法的跨平台加密的有效示例非常少。这是您访问 Gmail 等服务时 Google 所使用的算法。
本文可以帮助您实现非常强大的跨平台加密/解密。示例代码是 C++、C# 和 Java。但是,Java 通过 JNI (Java Native Interface) 调用,C# 通过 COM 调用,都可以调用原生的 C++ 代码,在我测试中,这比纯 Java 或 C# 实现快得多。尽管如此,有时人们还是希望在不调用原生 C++ 层的情况下完成。
对于 C#,为了实现 AES 256 GCM 加密,我使用了 Bouncy Castle 加密库。本文提供的代码片段在各种平台上进行加密和解密都可以完美工作。我已在 Linux (使用 Mono Framework) 和 Windows 上进行了测试。
对于 C++ 层,我使用了 Crypto++。这个库是跨平台兼容的 (Windows, Linux 和其他系统如 Solaris 等)。Crypto++ 是一个强大且实现非常完善的开源加密库。
本文无意为初学者准备,也不是为了教授 AES GCM 算法。
本文旨在提供一个您可以根据自己的修改来实现的示例代码。
C++ 有点复杂。下载 Crypto++ 源代码。创建一个控制台项目,并将现有的 Crypto++ 项目添加到解决方案中。然后将您的控制台项目设置为启动项目,并设置生成依赖顺序。
复制代码并更正头文件路径(例如 pch.h)。您也可以在项目属性中添加源文件目录来修复路径。同样,您可以更正 Crypto++ 编译库的路径或将其添加到您的项目属性中。
本文的另一个目的是将三种主要编程语言的示例代码集中在一个地方。您不必搜索数千个单独的示例,其中一些示例可能无法按预期工作。这里的代码示例可以毫无问题地工作。
最近,一个基于 Linux 的动态库和一个解释如何使用它的测试程序已上传到 GitHub。Linux x64 上的 C++ 可以通过创建一个包装器来用于 NodeJS。同样,它也可以轻松地用于 Apple 机器。
背景
跨平台 AES 256 GCM 加密和解密 (C++, C# 和 Java)
您还可以通过 这里 阅读更多关于 Crypto++ AES GCM 实现或算法本身的信息,以及通过 这里。
同样,关于 BouncyCastle 的详细信息可以在 这里 找到。
C# 代码中使用的 BouncyCastle .NET 可以在 这里 找到。
GitHub
Using the Code
对于 C#
请添加引用
BouncyCastle.Crypto
(BouncyCastle.Crypto.dll)
https://nuget.net.cn/packages/BouncyCastle.Crypto.dll/
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# 加密/解密类
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;
}
}
}
对于 Java
请将以下 JAR 添加到您的 Java 项目或类路径中
bcprov-jdk15on-160.jar
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 加密/解密类
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;
}
}
对于 C++ (Windows)
请将 Crypto++ 项目包含在您的测试项目中。
更正包含和链接库的路径
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 库 (GNU C++ 14 标准)
此仓库包含 2 个 Apache Netbeans 项目,在此 处。请克隆仓库并在 Netbeans IDE 中打开项目。您必须已配置 Netbeans IDE 才能编辑 C++ 项目。
构建步骤
Crypto++
要构建,请在 **终端** 中打开 cryptopp870 文件夹,然后输入 make
命令。它将构建 libcryptopp.a 静态库。该库默认使用操作系统架构。 GcmAes Netbeans 项目依赖于 libcryptopp.a 并与之链接。
一旦库可用,您就可以单独从 Netbeans IDE 构建这两个项目。GcmAesTest
项目具有 libGcmAes.so
依赖项,并且会预先构建。构建完成后,还可以调试项目。
测试程序验证了几项加密和解密测试。其中之一是解密使用 Java 代码加密的文本(链接如下)。
它产生以下示例测试结果
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
该测试项目还演示了如何在 Linux 类系统上将 libGcmAes.so 与其他 C++ 项目一起使用。通常,只需在您的项目中包含 GcmAes.h,并将您的项目链接到 libGcmAes.so。代码使用 C++ 14 标准。
GcmAes.h
/*
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
测试程序
/*
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
该项目涵盖了与上面 Linux 库项目完全相同的全部功能、测试和使用场景。
请访问 GitHub 上的 Windows C++ x64 DLL 和示例测试程序。
Node 和 C# (.Net Core) 包装器,使用 libGcmAes.so
这两个项目提供包装器类和测试项目,用于调用与加密和解密相关的 libGcmAes.so 导出的函数。Docker 容器 (Linux x64) 可以使用这些包装器来实现 AES 256 GCM 跨平台加密和解密。
请访问 GitHub 上的 NodeJS 和 .Net Core 包装器。
历史
- 2018年10月29日:首次发布
- 2023年4月12日:发布 Linux 库源代码
- 2023年4月13日:公开 Hex 和 Base64 编码和解码。添加更多测试用例
- 2023年4月15日:添加 Windows x64 C++ DLL GitHub 项目链接。它涵盖了与 Linux 库完全相同的功能和测试用例
- 2023年6月3日:添加 NodeJS 和 .Net Core GitHub 项目链接。