在 C# 中使用 OpenSSL 解密由 PHP 使用 AES256-GCM 加密的字符串
Decrypting a string encrypted by PHP with AES256-GCM using OpenSSL in C#
我正在对 PHP 中的 encrypt/decrypt 字符串使用 openssl:
function str_encryptaesgcm($plaintext, $password, $encoding = null) {
$aes = array("key" => substr(hash("sha256", $password, true), 0, 32), "cipher" => "aes-256-gcm", "iv" => openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-gcm")));
$encryptedstring = openssl_encrypt($plaintext, $aes["cipher"], $aes["key"], OPENSSL_RAW_DATA, $aes["iv"], $aes["tag"], "", 16);
return $encoding == "hex" ? bin2hex($aes["iv"].$encryptedstring.$aes["tag"]) : ($encoding == "base64" ? base64_encode($aes["iv"].$encryptedstring.$aes["tag"]) : $aes["iv"].$encryptedstring.$aes["tag"]);
}
function str_decryptaesgcm($encryptedstring, $password, $encoding = null) {
$encryptedstring = $encoding == "hex" ? hex2bin($encryptedstring) : ($encoding == "base64" ? base64_decode($encryptedstring) : $encryptedstring);
$aes = array("key" => substr(hash("sha256", $password, true), 0, 32), "cipher" => "aes-256-gcm", "ivlength" => openssl_cipher_iv_length("aes-256-gcm"), "iv" => substr($encryptedstring, 0, openssl_cipher_iv_length("aes-256-gcm")), "tag" => substr($encryptedstring, -16));
return openssl_decrypt(substr($encryptedstring, $aes["ivlength"], -16), $aes["cipher"], $aes["key"], OPENSSL_RAW_DATA, $aes["iv"], $aes["tag"]);
}
一切正常,事实上我得到:
$text = "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...";
$pass = "A random password to encrypt";
$enc = str_encryptaesgcm($text, $pass, "base64"); // OUTPUT: TrbntVEj8GEGeLE6ZYJnDIXnqSese5biWn604NePb2r6jsFhuzJsNHnN2GCizrGfhP4W39tahrGj0tORxvUbDpGT76WHr/v2wmnHHHiDGyjeKlWLu9/gfeualYvhsNF/N9inSpqxE2lQ+/vwpUJKYJw3bfo7DoGPDNk=
$dec = str_decryptaesgcm($enc, $pass, "base64"); // OUTPUT: Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...
不幸的是,我需要从 C# 中解密字符串,因此我使用 BouncyCastle 来执行此操作,这是我使用的 class:
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PoGORaidEngine.Crypto
{
internal static class AESGCM
{
private const int KEY_BIT_SIZE = 256;
private const int MAC_BIT_SIZE = 128;
private const int NONCE_BIT_SIZE = 96; // 12 bytes (openssl)
internal static string DecryptString(string EncryptedString, string Password)
{
if (string.IsNullOrEmpty(EncryptedString))
return string.Empty;
byte[] Key = Encoding.UTF8.GetBytes(SHA256String(Password).Substring(0, 32));
byte[] EncryptedData = Convert.FromBase64String(EncryptedString);
if (Key == null || Key.Length != KEY_BIT_SIZE / 8)
throw new ArgumentException(string.Format("Key needs to be {0} bit.", KEY_BIT_SIZE), "Key");
using (MemoryStream MStream = new MemoryStream(EncryptedData))
using (BinaryReader Binary = new BinaryReader(MStream))
{
byte[] IV = Binary.ReadBytes(NONCE_BIT_SIZE / 8);
GcmBlockCipher Cipher = new GcmBlockCipher(new AesEngine());
Cipher.Init(false, new AeadParameters(new KeyParameter(Key), MAC_BIT_SIZE, IV));
byte[] CipherText = Binary.ReadBytes(EncryptedData.Length - IV.Length);
byte[] PlainText = new byte[Cipher.GetOutputSize(CipherText.Length)];
int Length = Cipher.ProcessBytes(CipherText, 0, CipherText.Length, PlainText, 0);
Cipher.DoFinal(PlainText, Length);
return Encoding.UTF8.GetString(PlainText);
}
}
private static string SHA256String(string Password)
{
using (SHA256 Hash = SHA256.Create())
{
byte[] PasswordBytes = Hash.ComputeHash(Encoding.UTF8.GetBytes(Password));
StringBuilder SB = new StringBuilder();
for (int i = 0; i < PasswordBytes.Length; i++)
SB.Append(PasswordBytes[i].ToString("X2"));
return SB.ToString();
}
}
}
}
但是当我调用解密方法时抛出以下异常:
Org.BouncyCastle.Crypto.InvalidCipherTextException: mac check in GCM failed
我浪费了几个小时试图找出问题但没有成功,我也尝试在此处搜索,在 Whosebug 上,但没有找到任何答案回答我的问题,甚至这个 。
是否有人使用 AES256-GCM 测试并尝试使用 BouncyCastle 从 PHP (openssl) 解密到 C#?在此先感谢您的帮助。
更新
我已经尝试更新 PHP 加密方法以查看数据是否正常:
function str_encryptaesgcm($plaintext, $password, $encoding = null) {
$aes = array("key" => substr(hash("sha256", $password, true), 0, 32), "cipher" => "aes-256-gcm", "iv" => openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-gcm")));
$encryptedstring = openssl_encrypt($plaintext, $aes["cipher"], $aes["key"], OPENSSL_RAW_DATA, $aes["iv"], $aes["tag"], "", 16);
switch ($encoding) {
case "base64":
return array("encrypteddata" => base64_encode($aes["iv"].$encryptedstring.$aes["tag"]), "iv" => base64_encode($aes["iv"]), "encryptedstring" => base64_encode($encryptedstring), "tag" => base64_encode($aes["tag"]));
case "hex":
return array("encrypteddata" => bin2hex($aes["iv"].$encryptedstring.$aes["tag"]), "iv" => bin2hex($aes["iv"]), "encryptedstring" => bin2hex($encryptedstring), "tag" => bin2hex($aes["tag"]));
default:
return array("encrypteddata" => $aes["iv"].$encryptedstring.$aes["tag"], "iv" => $aes["iv"], "encryptedstring" => $encryptedstring, "tag" => $aes["tag"]);
}
}
所以我得到:
{"encrypteddata":"w2eUXD41sCgTBvN7PtKlhzHB0lodPohnOh8V1lWsATvRujwsV18DDftGqJLqpsWxatYUX7C0jxjLcPQUoazIfiVdRmAsbGAKuvXYSsNjQ6ahGY4AxowAp0p\/IGDuYWbCrof6GZHUyoxv9Ry8NP1yxNItnBlUhGS8ua0=","iv":"w2eUXD41sCgTBvN7","encryptedstring":"PtKlhzHB0lodPohnOh8V1lWsATvRujwsV18DDftGqJLqpsWxatYUX7C0jxjLcPQUoazIfiVdRmAsbGAKuvXYSsNjQ6ahGY4AxowAp0p\/IGDuYWbCrof6GZHUyoxv9Q==","tag":"HLw0\/XLE0i2cGVSEZLy5rQ=="}
这让我明白在 PHP 方面一切正常,还因为当我从 PHP 执行解密时一切正常。我尝试按照 Micheal Fehr 的建议更新 C# 代码上的 class,但是我得到一个新的异常:Org.BouncyCastle.Crypto.InvalidCipherTextException: data too short
:
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PoGORaidEngine.Crypto
{
internal static class AESGCM
{
private const int MAC_BIT_SIZE = 128;
private const int NONCE_BIT_SIZE = 96;
internal static string DecryptString(string EncryptedString, string Password)
{
if (string.IsNullOrEmpty(EncryptedString))
return string.Empty;
byte[] EncryptedData = Convert.FromBase64String(EncryptedString);
byte[] Key = DerivateKey(Password);
byte[] IV;
byte[] CipherText;
byte[] Tag;
using (MemoryStream MStream = new MemoryStream(EncryptedData))
using (BinaryReader Binary = new BinaryReader(MStream))
{
IV = Binary.ReadBytes(NONCE_BIT_SIZE / 8);
CipherText = Binary.ReadBytes(EncryptedData.Length - IV.Length - (MAC_BIT_SIZE / 8));
Tag = Binary.ReadBytes(MAC_BIT_SIZE / 8);
}
byte[] AAED = new byte[0];
byte[] DecryptedData = new byte[CipherText.Length];
GcmBlockCipher Cipher = new GcmBlockCipher(new AesEngine());
Cipher.Init(false, new AeadParameters(new KeyParameter(Key), MAC_BIT_SIZE, IV, AAED));
int Length = Cipher.ProcessBytes(CipherText, 0, CipherText.Length, DecryptedData, 0);
Cipher.DoFinal(DecryptedData, Length);
return Encoding.UTF8.GetString(DecryptedData);
}
private static byte[] DerivateKey(string Password)
{
using (SHA256 Hash = SHA256.Create())
return Hash.ComputeHash(Encoding.UTF8.GetBytes(Password));
}
}
}
作为反测试,我尝试进入 base64 IV,清理加密字符串和标签,数据完美匹配,如 PHP。
我确定解决方案非常接近。问题出现在:Cipher.DoFinal(DecryptedData, Length);
(new byte[CipherText.Length]
).
--- 解决方案 ---
注意:我已将简单的 SHA256 派生密钥替换为 PBKDF2-SHA512(具有 20K 次迭代)以提高安全性。
PHP 函数:
function str_encryptaesgcm($plaintext, $password, $encoding = null) {
$keysalt = openssl_random_pseudo_bytes(16);
$key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-gcm"));
$tag = "";
$encryptedstring = openssl_encrypt($plaintext, "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag, "", 16);
return $encoding == "hex" ? bin2hex($keysalt.$iv.$encryptedstring.$tag) : ($encoding == "base64" ? base64_encode($keysalt.$iv.$encryptedstring.$tag) : $keysalt.$iv.$encryptedstring.$tag);
}
function str_decryptaesgcm($encryptedstring, $password, $encoding = null) {
$encryptedstring = $encoding == "hex" ? hex2bin($encryptedstring) : ($encoding == "base64" ? base64_decode($encryptedstring) : $encryptedstring);
$keysalt = substr($encryptedstring, 0, 16);
$key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
$ivlength = openssl_cipher_iv_length("aes-256-gcm");
$iv = substr($encryptedstring, 16, $ivlength);
$tag = substr($encryptedstring, -16);
return openssl_decrypt(substr($encryptedstring, 16 + $ivlength, -16), "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag);
}
C# Class(使用 .NET Core 3 或更高版本中可用的 AesGcm)
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PRBMono.Crypto
{
internal static class AES
{
private static readonly int NONCE_BITS_SIZE = AesGcm.NonceByteSizes.MaxSize;
private static readonly int SALTKEY_BITS_SIZE = AesGcm.TagByteSizes.MaxSize;
private static readonly int MAC_BITS_SIZE = AesGcm.TagByteSizes.MaxSize;
private static readonly int KEY_ITERATIONS = 20000;
internal static string EncryptString(string String, bool Base64Encode = true)
{
if (string.IsNullOrEmpty(String))
return null;
byte[] SaltKey = CryptoMethods.RandomBytes(SALTKEY_BITS_SIZE);
byte[] Key = CryptoMethods.PBKDF2DerivateKey(Server.Config.Server_AESKey, HashAlgorithmName.SHA512, SaltKey, KEY_ITERATIONS, 32);
using AesGcm Aes = new(Key);
byte[] Data = Encoding.UTF8.GetBytes(String);
byte[] CipherData = new byte[Data.Length];
byte[] IV = CryptoMethods.RandomBytes(NONCE_BITS_SIZE);
byte[] Tag = new byte[MAC_BITS_SIZE];
Aes.Encrypt(IV, Data, CipherData, Tag);
using MemoryStream MStream = new();
using (BinaryWriter Binary = new(MStream))
{
Binary.Write(SaltKey);
Binary.Write(IV);
Binary.Write(CipherData);
Binary.Write(Tag);
}
return Base64Encode ? Convert.ToBase64String(MStream.ToArray()) : CryptoMethods.ByteArrayToHex(MStream.ToArray()).ToLower();
}
internal static string DecryptString(string EncryptedString, bool Base64Encode = true)
{
if (string.IsNullOrEmpty(EncryptedString))
return string.Empty;
byte[] EncryptedData = Base64Encode ? Convert.FromBase64String(EncryptedString) : CryptoMethods.HexToByteArray(EncryptedString);
byte[] SaltKey, Key, IV, CipherData, Tag;
using (MemoryStream MStream = new(EncryptedData))
using (BinaryReader Binary = new(MStream))
{
SaltKey = Binary.ReadBytes(SALTKEY_BITS_SIZE);
Key = CryptoMethods.PBKDF2DerivateKey(Server.Config.Server_AESKey, HashAlgorithmName.SHA512, SaltKey, KEY_ITERATIONS, 32);
IV = Binary.ReadBytes(NONCE_BITS_SIZE);
CipherData = Binary.ReadBytes(EncryptedData.Length - SaltKey.Length - IV.Length - MAC_BITS_SIZE);
Tag = Binary.ReadBytes(MAC_BITS_SIZE);
}
using AesGcm Aes = new(Key);
byte[] DecryptedData = new byte[CipherData.Length];
Aes.Decrypt(IV, CipherData, Tag, DecryptedData);
return Encoding.UTF8.GetString(DecryptedData);
}
}
}
C# Class(使用 BouncyCastle):
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PoGORaidEngine.Crypto
{
internal static class AESGCM
{
private const int MAC_BIT_SIZE = 128;
private const int SALTKEY_BIT_SIZE = 128;
private const int NONCE_BIT_SIZE = 96;
internal static string DecryptString(string EncryptedString, string Password)
{
if (string.IsNullOrEmpty(EncryptedString))
return string.Empty;
byte[] EncryptedData = Convert.FromBase64String(EncryptedString);
byte[] SaltKey;
byte[] Key;
byte[] IV;
byte[] CipherText;
byte[] Tag;
using (MemoryStream MStream = new MemoryStream(EncryptedData))
using (BinaryReader Binary = new BinaryReader(MStream))
{
SaltKey = Binary.ReadBytes(SALTKEY_BIT_SIZE / 8);
Key = PBKDF2DerivateKey(Password, HashAlgorithmName.SHA512, SaltKey, 20000, 32);
IV = Binary.ReadBytes(NONCE_BIT_SIZE / 8);
CipherText = Binary.ReadBytes(EncryptedData.Length - SaltKey.Length - IV.Length - (MAC_BIT_SIZE / 8));
Tag = Binary.ReadBytes(MAC_BIT_SIZE / 8);
}
byte[] DecryptedData = new byte[CipherText.Length];
byte[] CipherTextTag = new byte[CipherText.Length + Tag.Length];
Buffer.BlockCopy(CipherText, 0, CipherTextTag, 0, CipherText.Length);
Buffer.BlockCopy(Tag, 0, CipherTextTag, CipherText.Length, Tag.Length);
GcmBlockCipher Cipher = new GcmBlockCipher(new AesEngine());
Cipher.Init(false, new AeadParameters(new KeyParameter(Key), MAC_BIT_SIZE, IV));
int Length = Cipher.ProcessBytes(CipherTextTag, 0, CipherTextTag.Length, DecryptedData, 0);
Cipher.DoFinal(DecryptedData, Length);
return Encoding.UTF8.GetString(DecryptedData);
}
private static byte[] PBKDF2DerivateKey(string Password, HashAlgorithmName Algorithm, byte[] Salt, int Iterations, int Length)
{
using (Rfc2898DeriveBytes DeriveBytes = new Rfc2898DeriveBytes(Password, Salt, Iterations, Algorithm))
return DeriveBytes.GetBytes(Length);
}
}
}
我不确定您在 C# 上的“密钥派生”是否像在 PHP 上一样工作,所以我使用了我自己的。另外,我使用了自己的解密函数,运行s 没有 Bouncy Castle,它可以成为您使用 Bouncy Castle 的良好基础。
请注意,使用 SHA 哈希的密钥派生是 UNSECURE,您应该使用 PBKDF2 之类的东西来完成此任务。
我正在使用样本输出
TrbntVEj8GEGeLE6ZYJnDIXnqSese5biWn604NePb2r6jsFhuzJsNHnN2GCizrGfhP4W39tahrGj0tORxvUbDpGT76WHr/v2wmnHHHiDGyjeKlWLu9/gfeualYvhsNF/N9inSpqxE2lQ+/vwpUJKYJw3bfo7DoGPDNk=
作为 C# 中解密函数的输入 - 这是结果:
AES GCM 256 String decryption
* * * Decryption * * *
ciphertext (Base64): TrbntVEj8GEGeLE6ZYJnDIXnqSese5biWn604NePb2r6jsFhuzJsNHnN2GCizrGfhP4W39tahrGj0tORxvUbDpGT76WHr/v2wmnHHHiDGyjeKlWLu9/gfeualYvhsNF/N9inSpqxE2lQ+/vwpUJKYJw3bfo7DoGPDNk=
plaintext: Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...
请注意,我的代码没有异常处理,仅用于教育目的,代码是 运行 .net 5 在线编译器 (https://dotnetfiddle.net/WvUkXf):
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
public class Program {
public static void Main() {
Console.WriteLine("AES GCM 256 String decryption");
// decryption
Console.WriteLine("\n* * * Decryption * * *");
string password = "A random password to encrypt";
//generate hash of password # # # this is UNSECURE # # #
SHA256 mySHA256 = SHA256.Create();
byte[] Key = mySHA256.ComputeHash(Encoding.UTF8.GetBytes(password));
// ciphertext taken from encryption function in PHP
string soCiphertextBase64 = "TrbntVEj8GEGeLE6ZYJnDIXnqSese5biWn604NePb2r6jsFhuzJsNHnN2GCizrGfhP4W39tahrGj0tORxvUbDpGT76WHr/v2wmnHHHiDGyjeKlWLu9/gfeualYvhsNF/N9inSpqxE2lQ+/vwpUJKYJw3bfo7DoGPDNk=";
Console.WriteLine("ciphertext (Base64): " + soCiphertextBase64);
string soDecryptedtext = soAesGcmDecryptFromBase64(Key, soCiphertextBase64);
Console.WriteLine("plaintext: " + soDecryptedtext);
}
static string soAesGcmDecryptFromBase64(byte[] key, string data) {
const int MAC_BIT_SIZE = 128;
const int NONCE_BIT_SIZE = 96; // 12 bytes (openssl)
byte[] EncryptedData = Convert.FromBase64String(data);
byte[] IV;
byte[] CipherText;
byte[] Tag;
using (MemoryStream MStream = new MemoryStream(EncryptedData))
using (BinaryReader Binary = new BinaryReader(MStream)) {
IV = Binary.ReadBytes(NONCE_BIT_SIZE / 8);
CipherText = Binary.ReadBytes(EncryptedData.Length - IV.Length - (MAC_BIT_SIZE / 8));
Tag = Binary.ReadBytes((MAC_BIT_SIZE / 8));
}
string decryptedtext;
byte[] associatedData = new byte[0];
byte[] decryptedData = new byte[CipherText.Length];
using(var cipher = new AesGcm(key)) {
cipher.Decrypt(IV, CipherText, Tag, decryptedData, associatedData);
decryptedtext = Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length);
return decryptedtext;
}
}
}
编辑:您是否注意到 GCM-Tag 从未在您更新的解密函数中使用?
Bouncy Castle 的 GCM 函数与 Java-pendant 的工作原理类似,需要一个密文,该密文附加了标签。
最后需要做一些字节数组的复制操作:
byte[] CipherTextTag = new byte[CipherText.Length + Tag.Length];
System.Buffer.BlockCopy(CipherText, 0, CipherTextTag, 0, CipherText.Length);
System.Buffer.BlockCopy(Tag, 0, CipherTextTag, CipherText.Length, Tag.Length);
int Length = Cipher.ProcessBytes(CipherTextTag, 0, CipherTextTag.Length, DecryptedData, 0);
使用这个“完整的密文”,您可以使用原始 PHP 代码得到 运行:
AES GCM 256 String decryption
* * * Decryption * * *
ciphertext (Base64): aV+gDmSBbi9PjOT9FD8LcuISbEQ5F3q0X8qzf3MKiDzxo12WQVirsnltbApLMMG9JScVfTXx7PJw7EVFoKz8JLMYLMu/JsRGcfvihSK+d/yeRTBEuJHL74Hv2Zr7b4CoMJhEUmYF3KT2Onlj4lI5ChOjmgXvpSev/xc=
plaintext: Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...
完整代码:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
public class Program {
public static void Main() {
Console.WriteLine("AES GCM 256 String decryption");
// decryption
Console.WriteLine("\n* * * Decryption * * *");
string password = "A random password to encrypt";
//generate hash of password # # # this is UNSECURE # # #
string soCiphertextBase64 = "aV+gDmSBbi9PjOT9FD8LcuISbEQ5F3q0X8qzf3MKiDzxo12WQVirsnltbApLMMG9JScVfTXx7PJw7EVFoKz8JLMYLMu/JsRGcfvihSK+d/yeRTBEuJHL74Hv2Zr7b4CoMJhEUmYF3KT2Onlj4lI5ChOjmgXvpSev/xc=";
Console.WriteLine("ciphertext (Base64): " + soCiphertextBase64);
string soDecryptedtextAsk = DecryptString(soCiphertextBase64, password);
Console.WriteLine("plaintext: " + soDecryptedtextAsk);
}
static string DecryptString(string EncryptedString, string Password)
{
const int MAC_BIT_SIZE = 128;
const int NONCE_BIT_SIZE = 96;
if (string.IsNullOrEmpty(EncryptedString))
return string.Empty;
byte[] EncryptedData = Convert.FromBase64String(EncryptedString);
byte[] Key = DerivateKey(Password);
byte[] IV;
byte[] CipherText;
byte[] Tag;
using (MemoryStream MStream = new MemoryStream(EncryptedData))
using (BinaryReader Binary = new BinaryReader(MStream))
{
IV = Binary.ReadBytes(NONCE_BIT_SIZE / 8);
CipherText = Binary.ReadBytes(EncryptedData.Length - IV.Length - (MAC_BIT_SIZE / 8));
Tag = Binary.ReadBytes(MAC_BIT_SIZE / 8);
}
byte[] AAED = new byte[0];
byte[] DecryptedData = new byte[CipherText.Length];
GcmBlockCipher Cipher = new GcmBlockCipher(new AesEngine());
Cipher.Init(false, new AeadParameters(new KeyParameter(Key), MAC_BIT_SIZE, IV, AAED));
// combine ciphertext + tag
byte[] CipherTextTag = new byte[CipherText.Length + Tag.Length];
System.Buffer.BlockCopy(CipherText, 0, CipherTextTag, 0, CipherText.Length);
System.Buffer.BlockCopy(Tag, 0, CipherTextTag, CipherText.Length, Tag.Length);
int Length = Cipher.ProcessBytes(CipherTextTag, 0, CipherTextTag.Length, DecryptedData, 0);
//int Length = Cipher.ProcessBytes(CipherText, 0, CipherText.Length, DecryptedData, 0);
Cipher.DoFinal(DecryptedData, Length);
return Encoding.UTF8.GetString(DecryptedData);
}
private static byte[] DerivateKey(string Password)
{
using (SHA256 Hash = SHA256.Create())
return Hash.ComputeHash(Encoding.UTF8.GetBytes(Password));
}
}
Marco - 我很好奇 - 这些是您使用的仅有的两种语言吗(PHP 和 C#)?我们已经使用 Java、C#、Go、Python、Ruby、C、Openssl、bouncycastle 等为 GCM(128 和 256)编写了一些跨语言/库,但还没有运行 解决您遇到的任何问题,但我们处理密钥管理的方式略有不同。我们还没有添加 PHP,但已经看到与 C 相比,OpenSSL 的某些 PHP 接口存在一些差异。
我正在对 PHP 中的 encrypt/decrypt 字符串使用 openssl:
function str_encryptaesgcm($plaintext, $password, $encoding = null) {
$aes = array("key" => substr(hash("sha256", $password, true), 0, 32), "cipher" => "aes-256-gcm", "iv" => openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-gcm")));
$encryptedstring = openssl_encrypt($plaintext, $aes["cipher"], $aes["key"], OPENSSL_RAW_DATA, $aes["iv"], $aes["tag"], "", 16);
return $encoding == "hex" ? bin2hex($aes["iv"].$encryptedstring.$aes["tag"]) : ($encoding == "base64" ? base64_encode($aes["iv"].$encryptedstring.$aes["tag"]) : $aes["iv"].$encryptedstring.$aes["tag"]);
}
function str_decryptaesgcm($encryptedstring, $password, $encoding = null) {
$encryptedstring = $encoding == "hex" ? hex2bin($encryptedstring) : ($encoding == "base64" ? base64_decode($encryptedstring) : $encryptedstring);
$aes = array("key" => substr(hash("sha256", $password, true), 0, 32), "cipher" => "aes-256-gcm", "ivlength" => openssl_cipher_iv_length("aes-256-gcm"), "iv" => substr($encryptedstring, 0, openssl_cipher_iv_length("aes-256-gcm")), "tag" => substr($encryptedstring, -16));
return openssl_decrypt(substr($encryptedstring, $aes["ivlength"], -16), $aes["cipher"], $aes["key"], OPENSSL_RAW_DATA, $aes["iv"], $aes["tag"]);
}
一切正常,事实上我得到:
$text = "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...";
$pass = "A random password to encrypt";
$enc = str_encryptaesgcm($text, $pass, "base64"); // OUTPUT: TrbntVEj8GEGeLE6ZYJnDIXnqSese5biWn604NePb2r6jsFhuzJsNHnN2GCizrGfhP4W39tahrGj0tORxvUbDpGT76WHr/v2wmnHHHiDGyjeKlWLu9/gfeualYvhsNF/N9inSpqxE2lQ+/vwpUJKYJw3bfo7DoGPDNk=
$dec = str_decryptaesgcm($enc, $pass, "base64"); // OUTPUT: Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...
不幸的是,我需要从 C# 中解密字符串,因此我使用 BouncyCastle 来执行此操作,这是我使用的 class:
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PoGORaidEngine.Crypto
{
internal static class AESGCM
{
private const int KEY_BIT_SIZE = 256;
private const int MAC_BIT_SIZE = 128;
private const int NONCE_BIT_SIZE = 96; // 12 bytes (openssl)
internal static string DecryptString(string EncryptedString, string Password)
{
if (string.IsNullOrEmpty(EncryptedString))
return string.Empty;
byte[] Key = Encoding.UTF8.GetBytes(SHA256String(Password).Substring(0, 32));
byte[] EncryptedData = Convert.FromBase64String(EncryptedString);
if (Key == null || Key.Length != KEY_BIT_SIZE / 8)
throw new ArgumentException(string.Format("Key needs to be {0} bit.", KEY_BIT_SIZE), "Key");
using (MemoryStream MStream = new MemoryStream(EncryptedData))
using (BinaryReader Binary = new BinaryReader(MStream))
{
byte[] IV = Binary.ReadBytes(NONCE_BIT_SIZE / 8);
GcmBlockCipher Cipher = new GcmBlockCipher(new AesEngine());
Cipher.Init(false, new AeadParameters(new KeyParameter(Key), MAC_BIT_SIZE, IV));
byte[] CipherText = Binary.ReadBytes(EncryptedData.Length - IV.Length);
byte[] PlainText = new byte[Cipher.GetOutputSize(CipherText.Length)];
int Length = Cipher.ProcessBytes(CipherText, 0, CipherText.Length, PlainText, 0);
Cipher.DoFinal(PlainText, Length);
return Encoding.UTF8.GetString(PlainText);
}
}
private static string SHA256String(string Password)
{
using (SHA256 Hash = SHA256.Create())
{
byte[] PasswordBytes = Hash.ComputeHash(Encoding.UTF8.GetBytes(Password));
StringBuilder SB = new StringBuilder();
for (int i = 0; i < PasswordBytes.Length; i++)
SB.Append(PasswordBytes[i].ToString("X2"));
return SB.ToString();
}
}
}
}
但是当我调用解密方法时抛出以下异常:
Org.BouncyCastle.Crypto.InvalidCipherTextException: mac check in GCM failed
我浪费了几个小时试图找出问题但没有成功,我也尝试在此处搜索,在 Whosebug 上,但没有找到任何答案回答我的问题,甚至这个
更新
我已经尝试更新 PHP 加密方法以查看数据是否正常:
function str_encryptaesgcm($plaintext, $password, $encoding = null) {
$aes = array("key" => substr(hash("sha256", $password, true), 0, 32), "cipher" => "aes-256-gcm", "iv" => openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-gcm")));
$encryptedstring = openssl_encrypt($plaintext, $aes["cipher"], $aes["key"], OPENSSL_RAW_DATA, $aes["iv"], $aes["tag"], "", 16);
switch ($encoding) {
case "base64":
return array("encrypteddata" => base64_encode($aes["iv"].$encryptedstring.$aes["tag"]), "iv" => base64_encode($aes["iv"]), "encryptedstring" => base64_encode($encryptedstring), "tag" => base64_encode($aes["tag"]));
case "hex":
return array("encrypteddata" => bin2hex($aes["iv"].$encryptedstring.$aes["tag"]), "iv" => bin2hex($aes["iv"]), "encryptedstring" => bin2hex($encryptedstring), "tag" => bin2hex($aes["tag"]));
default:
return array("encrypteddata" => $aes["iv"].$encryptedstring.$aes["tag"], "iv" => $aes["iv"], "encryptedstring" => $encryptedstring, "tag" => $aes["tag"]);
}
}
所以我得到:
{"encrypteddata":"w2eUXD41sCgTBvN7PtKlhzHB0lodPohnOh8V1lWsATvRujwsV18DDftGqJLqpsWxatYUX7C0jxjLcPQUoazIfiVdRmAsbGAKuvXYSsNjQ6ahGY4AxowAp0p\/IGDuYWbCrof6GZHUyoxv9Ry8NP1yxNItnBlUhGS8ua0=","iv":"w2eUXD41sCgTBvN7","encryptedstring":"PtKlhzHB0lodPohnOh8V1lWsATvRujwsV18DDftGqJLqpsWxatYUX7C0jxjLcPQUoazIfiVdRmAsbGAKuvXYSsNjQ6ahGY4AxowAp0p\/IGDuYWbCrof6GZHUyoxv9Q==","tag":"HLw0\/XLE0i2cGVSEZLy5rQ=="}
这让我明白在 PHP 方面一切正常,还因为当我从 PHP 执行解密时一切正常。我尝试按照 Micheal Fehr 的建议更新 C# 代码上的 class,但是我得到一个新的异常:Org.BouncyCastle.Crypto.InvalidCipherTextException: data too short
:
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PoGORaidEngine.Crypto
{
internal static class AESGCM
{
private const int MAC_BIT_SIZE = 128;
private const int NONCE_BIT_SIZE = 96;
internal static string DecryptString(string EncryptedString, string Password)
{
if (string.IsNullOrEmpty(EncryptedString))
return string.Empty;
byte[] EncryptedData = Convert.FromBase64String(EncryptedString);
byte[] Key = DerivateKey(Password);
byte[] IV;
byte[] CipherText;
byte[] Tag;
using (MemoryStream MStream = new MemoryStream(EncryptedData))
using (BinaryReader Binary = new BinaryReader(MStream))
{
IV = Binary.ReadBytes(NONCE_BIT_SIZE / 8);
CipherText = Binary.ReadBytes(EncryptedData.Length - IV.Length - (MAC_BIT_SIZE / 8));
Tag = Binary.ReadBytes(MAC_BIT_SIZE / 8);
}
byte[] AAED = new byte[0];
byte[] DecryptedData = new byte[CipherText.Length];
GcmBlockCipher Cipher = new GcmBlockCipher(new AesEngine());
Cipher.Init(false, new AeadParameters(new KeyParameter(Key), MAC_BIT_SIZE, IV, AAED));
int Length = Cipher.ProcessBytes(CipherText, 0, CipherText.Length, DecryptedData, 0);
Cipher.DoFinal(DecryptedData, Length);
return Encoding.UTF8.GetString(DecryptedData);
}
private static byte[] DerivateKey(string Password)
{
using (SHA256 Hash = SHA256.Create())
return Hash.ComputeHash(Encoding.UTF8.GetBytes(Password));
}
}
}
作为反测试,我尝试进入 base64 IV,清理加密字符串和标签,数据完美匹配,如 PHP。
我确定解决方案非常接近。问题出现在:Cipher.DoFinal(DecryptedData, Length);
(new byte[CipherText.Length]
).
--- 解决方案 ---
注意:我已将简单的 SHA256 派生密钥替换为 PBKDF2-SHA512(具有 20K 次迭代)以提高安全性。
PHP 函数:
function str_encryptaesgcm($plaintext, $password, $encoding = null) {
$keysalt = openssl_random_pseudo_bytes(16);
$key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-gcm"));
$tag = "";
$encryptedstring = openssl_encrypt($plaintext, "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag, "", 16);
return $encoding == "hex" ? bin2hex($keysalt.$iv.$encryptedstring.$tag) : ($encoding == "base64" ? base64_encode($keysalt.$iv.$encryptedstring.$tag) : $keysalt.$iv.$encryptedstring.$tag);
}
function str_decryptaesgcm($encryptedstring, $password, $encoding = null) {
$encryptedstring = $encoding == "hex" ? hex2bin($encryptedstring) : ($encoding == "base64" ? base64_decode($encryptedstring) : $encryptedstring);
$keysalt = substr($encryptedstring, 0, 16);
$key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
$ivlength = openssl_cipher_iv_length("aes-256-gcm");
$iv = substr($encryptedstring, 16, $ivlength);
$tag = substr($encryptedstring, -16);
return openssl_decrypt(substr($encryptedstring, 16 + $ivlength, -16), "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag);
}
C# Class(使用 .NET Core 3 或更高版本中可用的 AesGcm)
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PRBMono.Crypto
{
internal static class AES
{
private static readonly int NONCE_BITS_SIZE = AesGcm.NonceByteSizes.MaxSize;
private static readonly int SALTKEY_BITS_SIZE = AesGcm.TagByteSizes.MaxSize;
private static readonly int MAC_BITS_SIZE = AesGcm.TagByteSizes.MaxSize;
private static readonly int KEY_ITERATIONS = 20000;
internal static string EncryptString(string String, bool Base64Encode = true)
{
if (string.IsNullOrEmpty(String))
return null;
byte[] SaltKey = CryptoMethods.RandomBytes(SALTKEY_BITS_SIZE);
byte[] Key = CryptoMethods.PBKDF2DerivateKey(Server.Config.Server_AESKey, HashAlgorithmName.SHA512, SaltKey, KEY_ITERATIONS, 32);
using AesGcm Aes = new(Key);
byte[] Data = Encoding.UTF8.GetBytes(String);
byte[] CipherData = new byte[Data.Length];
byte[] IV = CryptoMethods.RandomBytes(NONCE_BITS_SIZE);
byte[] Tag = new byte[MAC_BITS_SIZE];
Aes.Encrypt(IV, Data, CipherData, Tag);
using MemoryStream MStream = new();
using (BinaryWriter Binary = new(MStream))
{
Binary.Write(SaltKey);
Binary.Write(IV);
Binary.Write(CipherData);
Binary.Write(Tag);
}
return Base64Encode ? Convert.ToBase64String(MStream.ToArray()) : CryptoMethods.ByteArrayToHex(MStream.ToArray()).ToLower();
}
internal static string DecryptString(string EncryptedString, bool Base64Encode = true)
{
if (string.IsNullOrEmpty(EncryptedString))
return string.Empty;
byte[] EncryptedData = Base64Encode ? Convert.FromBase64String(EncryptedString) : CryptoMethods.HexToByteArray(EncryptedString);
byte[] SaltKey, Key, IV, CipherData, Tag;
using (MemoryStream MStream = new(EncryptedData))
using (BinaryReader Binary = new(MStream))
{
SaltKey = Binary.ReadBytes(SALTKEY_BITS_SIZE);
Key = CryptoMethods.PBKDF2DerivateKey(Server.Config.Server_AESKey, HashAlgorithmName.SHA512, SaltKey, KEY_ITERATIONS, 32);
IV = Binary.ReadBytes(NONCE_BITS_SIZE);
CipherData = Binary.ReadBytes(EncryptedData.Length - SaltKey.Length - IV.Length - MAC_BITS_SIZE);
Tag = Binary.ReadBytes(MAC_BITS_SIZE);
}
using AesGcm Aes = new(Key);
byte[] DecryptedData = new byte[CipherData.Length];
Aes.Decrypt(IV, CipherData, Tag, DecryptedData);
return Encoding.UTF8.GetString(DecryptedData);
}
}
}
C# Class(使用 BouncyCastle):
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PoGORaidEngine.Crypto
{
internal static class AESGCM
{
private const int MAC_BIT_SIZE = 128;
private const int SALTKEY_BIT_SIZE = 128;
private const int NONCE_BIT_SIZE = 96;
internal static string DecryptString(string EncryptedString, string Password)
{
if (string.IsNullOrEmpty(EncryptedString))
return string.Empty;
byte[] EncryptedData = Convert.FromBase64String(EncryptedString);
byte[] SaltKey;
byte[] Key;
byte[] IV;
byte[] CipherText;
byte[] Tag;
using (MemoryStream MStream = new MemoryStream(EncryptedData))
using (BinaryReader Binary = new BinaryReader(MStream))
{
SaltKey = Binary.ReadBytes(SALTKEY_BIT_SIZE / 8);
Key = PBKDF2DerivateKey(Password, HashAlgorithmName.SHA512, SaltKey, 20000, 32);
IV = Binary.ReadBytes(NONCE_BIT_SIZE / 8);
CipherText = Binary.ReadBytes(EncryptedData.Length - SaltKey.Length - IV.Length - (MAC_BIT_SIZE / 8));
Tag = Binary.ReadBytes(MAC_BIT_SIZE / 8);
}
byte[] DecryptedData = new byte[CipherText.Length];
byte[] CipherTextTag = new byte[CipherText.Length + Tag.Length];
Buffer.BlockCopy(CipherText, 0, CipherTextTag, 0, CipherText.Length);
Buffer.BlockCopy(Tag, 0, CipherTextTag, CipherText.Length, Tag.Length);
GcmBlockCipher Cipher = new GcmBlockCipher(new AesEngine());
Cipher.Init(false, new AeadParameters(new KeyParameter(Key), MAC_BIT_SIZE, IV));
int Length = Cipher.ProcessBytes(CipherTextTag, 0, CipherTextTag.Length, DecryptedData, 0);
Cipher.DoFinal(DecryptedData, Length);
return Encoding.UTF8.GetString(DecryptedData);
}
private static byte[] PBKDF2DerivateKey(string Password, HashAlgorithmName Algorithm, byte[] Salt, int Iterations, int Length)
{
using (Rfc2898DeriveBytes DeriveBytes = new Rfc2898DeriveBytes(Password, Salt, Iterations, Algorithm))
return DeriveBytes.GetBytes(Length);
}
}
}
我不确定您在 C# 上的“密钥派生”是否像在 PHP 上一样工作,所以我使用了我自己的。另外,我使用了自己的解密函数,运行s 没有 Bouncy Castle,它可以成为您使用 Bouncy Castle 的良好基础。
请注意,使用 SHA 哈希的密钥派生是 UNSECURE,您应该使用 PBKDF2 之类的东西来完成此任务。
我正在使用样本输出
TrbntVEj8GEGeLE6ZYJnDIXnqSese5biWn604NePb2r6jsFhuzJsNHnN2GCizrGfhP4W39tahrGj0tORxvUbDpGT76WHr/v2wmnHHHiDGyjeKlWLu9/gfeualYvhsNF/N9inSpqxE2lQ+/vwpUJKYJw3bfo7DoGPDNk=
作为 C# 中解密函数的输入 - 这是结果:
AES GCM 256 String decryption
* * * Decryption * * *
ciphertext (Base64): TrbntVEj8GEGeLE6ZYJnDIXnqSese5biWn604NePb2r6jsFhuzJsNHnN2GCizrGfhP4W39tahrGj0tORxvUbDpGT76WHr/v2wmnHHHiDGyjeKlWLu9/gfeualYvhsNF/N9inSpqxE2lQ+/vwpUJKYJw3bfo7DoGPDNk=
plaintext: Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...
请注意,我的代码没有异常处理,仅用于教育目的,代码是 运行 .net 5 在线编译器 (https://dotnetfiddle.net/WvUkXf):
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
public class Program {
public static void Main() {
Console.WriteLine("AES GCM 256 String decryption");
// decryption
Console.WriteLine("\n* * * Decryption * * *");
string password = "A random password to encrypt";
//generate hash of password # # # this is UNSECURE # # #
SHA256 mySHA256 = SHA256.Create();
byte[] Key = mySHA256.ComputeHash(Encoding.UTF8.GetBytes(password));
// ciphertext taken from encryption function in PHP
string soCiphertextBase64 = "TrbntVEj8GEGeLE6ZYJnDIXnqSese5biWn604NePb2r6jsFhuzJsNHnN2GCizrGfhP4W39tahrGj0tORxvUbDpGT76WHr/v2wmnHHHiDGyjeKlWLu9/gfeualYvhsNF/N9inSpqxE2lQ+/vwpUJKYJw3bfo7DoGPDNk=";
Console.WriteLine("ciphertext (Base64): " + soCiphertextBase64);
string soDecryptedtext = soAesGcmDecryptFromBase64(Key, soCiphertextBase64);
Console.WriteLine("plaintext: " + soDecryptedtext);
}
static string soAesGcmDecryptFromBase64(byte[] key, string data) {
const int MAC_BIT_SIZE = 128;
const int NONCE_BIT_SIZE = 96; // 12 bytes (openssl)
byte[] EncryptedData = Convert.FromBase64String(data);
byte[] IV;
byte[] CipherText;
byte[] Tag;
using (MemoryStream MStream = new MemoryStream(EncryptedData))
using (BinaryReader Binary = new BinaryReader(MStream)) {
IV = Binary.ReadBytes(NONCE_BIT_SIZE / 8);
CipherText = Binary.ReadBytes(EncryptedData.Length - IV.Length - (MAC_BIT_SIZE / 8));
Tag = Binary.ReadBytes((MAC_BIT_SIZE / 8));
}
string decryptedtext;
byte[] associatedData = new byte[0];
byte[] decryptedData = new byte[CipherText.Length];
using(var cipher = new AesGcm(key)) {
cipher.Decrypt(IV, CipherText, Tag, decryptedData, associatedData);
decryptedtext = Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length);
return decryptedtext;
}
}
}
编辑:您是否注意到 GCM-Tag 从未在您更新的解密函数中使用?
Bouncy Castle 的 GCM 函数与 Java-pendant 的工作原理类似,需要一个密文,该密文附加了标签。
最后需要做一些字节数组的复制操作:
byte[] CipherTextTag = new byte[CipherText.Length + Tag.Length];
System.Buffer.BlockCopy(CipherText, 0, CipherTextTag, 0, CipherText.Length);
System.Buffer.BlockCopy(Tag, 0, CipherTextTag, CipherText.Length, Tag.Length);
int Length = Cipher.ProcessBytes(CipherTextTag, 0, CipherTextTag.Length, DecryptedData, 0);
使用这个“完整的密文”,您可以使用原始 PHP 代码得到 运行:
AES GCM 256 String decryption
* * * Decryption * * *
ciphertext (Base64): aV+gDmSBbi9PjOT9FD8LcuISbEQ5F3q0X8qzf3MKiDzxo12WQVirsnltbApLMMG9JScVfTXx7PJw7EVFoKz8JLMYLMu/JsRGcfvihSK+d/yeRTBEuJHL74Hv2Zr7b4CoMJhEUmYF3KT2Onlj4lI5ChOjmgXvpSev/xc=
plaintext: Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...
完整代码:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
public class Program {
public static void Main() {
Console.WriteLine("AES GCM 256 String decryption");
// decryption
Console.WriteLine("\n* * * Decryption * * *");
string password = "A random password to encrypt";
//generate hash of password # # # this is UNSECURE # # #
string soCiphertextBase64 = "aV+gDmSBbi9PjOT9FD8LcuISbEQ5F3q0X8qzf3MKiDzxo12WQVirsnltbApLMMG9JScVfTXx7PJw7EVFoKz8JLMYLMu/JsRGcfvihSK+d/yeRTBEuJHL74Hv2Zr7b4CoMJhEUmYF3KT2Onlj4lI5ChOjmgXvpSev/xc=";
Console.WriteLine("ciphertext (Base64): " + soCiphertextBase64);
string soDecryptedtextAsk = DecryptString(soCiphertextBase64, password);
Console.WriteLine("plaintext: " + soDecryptedtextAsk);
}
static string DecryptString(string EncryptedString, string Password)
{
const int MAC_BIT_SIZE = 128;
const int NONCE_BIT_SIZE = 96;
if (string.IsNullOrEmpty(EncryptedString))
return string.Empty;
byte[] EncryptedData = Convert.FromBase64String(EncryptedString);
byte[] Key = DerivateKey(Password);
byte[] IV;
byte[] CipherText;
byte[] Tag;
using (MemoryStream MStream = new MemoryStream(EncryptedData))
using (BinaryReader Binary = new BinaryReader(MStream))
{
IV = Binary.ReadBytes(NONCE_BIT_SIZE / 8);
CipherText = Binary.ReadBytes(EncryptedData.Length - IV.Length - (MAC_BIT_SIZE / 8));
Tag = Binary.ReadBytes(MAC_BIT_SIZE / 8);
}
byte[] AAED = new byte[0];
byte[] DecryptedData = new byte[CipherText.Length];
GcmBlockCipher Cipher = new GcmBlockCipher(new AesEngine());
Cipher.Init(false, new AeadParameters(new KeyParameter(Key), MAC_BIT_SIZE, IV, AAED));
// combine ciphertext + tag
byte[] CipherTextTag = new byte[CipherText.Length + Tag.Length];
System.Buffer.BlockCopy(CipherText, 0, CipherTextTag, 0, CipherText.Length);
System.Buffer.BlockCopy(Tag, 0, CipherTextTag, CipherText.Length, Tag.Length);
int Length = Cipher.ProcessBytes(CipherTextTag, 0, CipherTextTag.Length, DecryptedData, 0);
//int Length = Cipher.ProcessBytes(CipherText, 0, CipherText.Length, DecryptedData, 0);
Cipher.DoFinal(DecryptedData, Length);
return Encoding.UTF8.GetString(DecryptedData);
}
private static byte[] DerivateKey(string Password)
{
using (SHA256 Hash = SHA256.Create())
return Hash.ComputeHash(Encoding.UTF8.GetBytes(Password));
}
}
Marco - 我很好奇 - 这些是您使用的仅有的两种语言吗(PHP 和 C#)?我们已经使用 Java、C#、Go、Python、Ruby、C、Openssl、bouncycastle 等为 GCM(128 和 256)编写了一些跨语言/库,但还没有运行 解决您遇到的任何问题,但我们处理密钥管理的方式略有不同。我们还没有添加 PHP,但已经看到与 C 相比,OpenSSL 的某些 PHP 接口存在一些差异。