如何将 python AES 函数转换为 C# 代码
How to turn python AES function into C# code
我有一部分 python 代码想用在我的 C# 项目中,但我找不到正确的方法来实现它。
python代码:
def getCiphertext(plaintext, key = key_, cfb_iv = iv_, size = 128):
message = plaintext.encode('utf-8')
cfb_cipher_encrypt = AES.new(key, AES.MODE_CFB, cfb_iv, segment_size = size)
mid = cfb_cipher_encrypt.encrypt(message)
return hexlify(mid).decode()
我尝试了下面的 C# 代码,但结果不同:
using System.Security.Cryptography;
public static string AesEncrypt(string str, string key, string IVString)
{
Encoding encoder = Encoding.UTF8;
byte[] toEncryptArray = Encoding.UTF8.GetBytes(str);
RijndaelManaged rm = new RijndaelManaged
{
Key = encoder.GetBytes(key),
Mode = CipherMode.CFB,
BlockSize = 128,
Padding = PaddingMode.PKCS7,
IV = encoder.GetBytes(IVString),
};
ICryptoTransform cTransform = rm.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return ToBCDStringLower(resultArray);//result
}
public static string ToBCDStringLower(byte[] buffer)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < buffer.Length; i++)
{
sb.Append(buffer[i].ToString("x2"));
}
return sb.ToString();
}
谢谢大家!
.NET 的 CFB 实现:
.NET 中的 CFB 存在问题。在 .NET Framework 中它受支持,在 .NET Core 中仅从 .NET 5.0 开始。
此外,.NET Framework 和.NET Core 允许不同的段大小,但都支持 8 位和 128 位,这对应于最常见的变体,即 CFB8 和 CFB128(或全块 CFB)。段大小是 CFB 中的一个附加参数,它对应于每个加密步骤加密的位,请参阅 CFB。
另一个特点是在 .NET 中明文大小必须是段大小的整数倍。这很了不起(实际上已经是一个错误),因为 CFB 是一种不需要填充的流密码模式。
所以对于CFB,除了CFB8,一般都需要padding。在 CFB128 到完整块的情况下,允许应用默认填充 PKCS7。
因此,要得到与未填充的明文对应的密文,必须将密文截断为明文大小。
Python与C#代码的比较:
在发布的 Python 代码中,参数列表中的段大小默认为 128 位(PyCryptodome 默认为 8 位)。在C#代码中,没有指定段大小(这里表示为FeedbackSize
),所以使用默认值128位。
因此,除非在 Python 代码中明确指定了 128 位以外的段大小,否则两个代码都应用相同的段大小。
此外,在 C# 代码中,填充 (PKCS7) 是根据 C# 实现的要求完成的。因此,将C#代码的密文截断为明文大小时,匹配Python代码的密文
以下示例使用您发布的代码未更改:
string plaintext = "The quick brown fox jumps over the lazy dog";
string key = "01234567890123456789012345678901";
string cfb_iv = "0123456789012345";
string ciphertext = AesEncrypt(plaintext, key, cfb_iv);
string ciphertextTrunc = ciphertext.Substring(0, plaintext.Length * 2); // *2 since AesEncryptOP returns the ciphertext hex encoded
Console.WriteLine(ciphertext);
Console.WriteLine(ciphertextTrunc);
输出:
09f1e464983a7d25305d5b865386e477d97b34b9a6365372ef83b78e495692489c1848a124345eb808eb66d268c6d1ad
09f1e464983a7d25305d5b865386e477d97b34b9a6365372ef83b78e495692489c1848a124345eb808eb66
如您所见,缩短的密文对应于 Python 代码的输出。
请注意,如第 1 部分所述,CFB128 需要填充。将填充更改为 PaddingMode.None
将导致 CryptographicException:输入数据不是完整的块。但是,使用 CFB8 这将是可能的。
充气城堡:
.NET 内置实现的替代方法是 BouncyCastle,它将 CFB 实现为流密码模式,因此不需要填充。以下代码:
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto;
...
public static string Encrypt(string str, string keyString, string IVString)
{
byte[] inputBytes = Encoding.UTF8.GetBytes(str);
byte[] IV = Encoding.UTF8.GetBytes(IVString);
byte[] key = Encoding.UTF8.GetBytes(keyString);
AesEngine engine = new AesEngine();
CfbBlockCipher blockCipher = new CfbBlockCipher(engine, 128);
BufferedBlockCipher cipher = new BufferedBlockCipher(blockCipher);
KeyParameter keyParam = new KeyParameter(key);
ParametersWithIV keyParamWithIv = new ParametersWithIV(keyParam, IV);
cipher.Init(true, keyParamWithIv);
byte[] outputBytes = new byte[cipher.GetOutputSize(inputBytes.Length)];
int length = cipher.ProcessBytes(inputBytes, outputBytes, 0);
cipher.DoFinal(outputBytes, length);
string encryptedInput = ToBCDStringLower(outputBytes);
return encryptedInput;
}
直接(即不截断)returnsPython代码的结果。
我有一部分 python 代码想用在我的 C# 项目中,但我找不到正确的方法来实现它。
python代码:
def getCiphertext(plaintext, key = key_, cfb_iv = iv_, size = 128):
message = plaintext.encode('utf-8')
cfb_cipher_encrypt = AES.new(key, AES.MODE_CFB, cfb_iv, segment_size = size)
mid = cfb_cipher_encrypt.encrypt(message)
return hexlify(mid).decode()
我尝试了下面的 C# 代码,但结果不同:
using System.Security.Cryptography;
public static string AesEncrypt(string str, string key, string IVString)
{
Encoding encoder = Encoding.UTF8;
byte[] toEncryptArray = Encoding.UTF8.GetBytes(str);
RijndaelManaged rm = new RijndaelManaged
{
Key = encoder.GetBytes(key),
Mode = CipherMode.CFB,
BlockSize = 128,
Padding = PaddingMode.PKCS7,
IV = encoder.GetBytes(IVString),
};
ICryptoTransform cTransform = rm.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return ToBCDStringLower(resultArray);//result
}
public static string ToBCDStringLower(byte[] buffer)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < buffer.Length; i++)
{
sb.Append(buffer[i].ToString("x2"));
}
return sb.ToString();
}
谢谢大家!
.NET 的 CFB 实现:
.NET 中的 CFB 存在问题。在 .NET Framework 中它受支持,在 .NET Core 中仅从 .NET 5.0 开始。
此外,.NET Framework 和.NET Core 允许不同的段大小,但都支持 8 位和 128 位,这对应于最常见的变体,即 CFB8 和 CFB128(或全块 CFB)。段大小是 CFB 中的一个附加参数,它对应于每个加密步骤加密的位,请参阅 CFB。
另一个特点是在 .NET 中明文大小必须是段大小的整数倍。这很了不起(实际上已经是一个错误),因为 CFB 是一种不需要填充的流密码模式。
所以对于CFB,除了CFB8,一般都需要padding。在 CFB128 到完整块的情况下,允许应用默认填充 PKCS7。
因此,要得到与未填充的明文对应的密文,必须将密文截断为明文大小。
Python与C#代码的比较:
在发布的 Python 代码中,参数列表中的段大小默认为 128 位(PyCryptodome 默认为 8 位)。在C#代码中,没有指定段大小(这里表示为FeedbackSize
),所以使用默认值128位。
因此,除非在 Python 代码中明确指定了 128 位以外的段大小,否则两个代码都应用相同的段大小。
此外,在 C# 代码中,填充 (PKCS7) 是根据 C# 实现的要求完成的。因此,将C#代码的密文截断为明文大小时,匹配Python代码的密文
以下示例使用您发布的代码未更改:
string plaintext = "The quick brown fox jumps over the lazy dog";
string key = "01234567890123456789012345678901";
string cfb_iv = "0123456789012345";
string ciphertext = AesEncrypt(plaintext, key, cfb_iv);
string ciphertextTrunc = ciphertext.Substring(0, plaintext.Length * 2); // *2 since AesEncryptOP returns the ciphertext hex encoded
Console.WriteLine(ciphertext);
Console.WriteLine(ciphertextTrunc);
输出:
09f1e464983a7d25305d5b865386e477d97b34b9a6365372ef83b78e495692489c1848a124345eb808eb66d268c6d1ad
09f1e464983a7d25305d5b865386e477d97b34b9a6365372ef83b78e495692489c1848a124345eb808eb66
如您所见,缩短的密文对应于 Python 代码的输出。
请注意,如第 1 部分所述,CFB128 需要填充。将填充更改为 PaddingMode.None
将导致 CryptographicException:输入数据不是完整的块。但是,使用 CFB8 这将是可能的。
充气城堡:
.NET 内置实现的替代方法是 BouncyCastle,它将 CFB 实现为流密码模式,因此不需要填充。以下代码:
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto;
...
public static string Encrypt(string str, string keyString, string IVString)
{
byte[] inputBytes = Encoding.UTF8.GetBytes(str);
byte[] IV = Encoding.UTF8.GetBytes(IVString);
byte[] key = Encoding.UTF8.GetBytes(keyString);
AesEngine engine = new AesEngine();
CfbBlockCipher blockCipher = new CfbBlockCipher(engine, 128);
BufferedBlockCipher cipher = new BufferedBlockCipher(blockCipher);
KeyParameter keyParam = new KeyParameter(key);
ParametersWithIV keyParamWithIv = new ParametersWithIV(keyParam, IV);
cipher.Init(true, keyParamWithIv);
byte[] outputBytes = new byte[cipher.GetOutputSize(inputBytes.Length)];
int length = cipher.ProcessBytes(inputBytes, outputBytes, 0);
cipher.DoFinal(outputBytes, length);
string encryptedInput = ToBCDStringLower(outputBytes);
return encryptedInput;
}
直接(即不截断)returnsPython代码的结果。