使用特定密钥的 AES 解密不起作用



我有一个PowerShell脚本(如下(,它使用特定密钥(作为字符串(成功加密和解密字符串值。

现在,我正在尝试编写一个 C# 方法,该方法可以使用相同的密钥解密值,但我不确定如何正确使用 PasswordDeriveBytes。

此外,我正在尝试为 AesManaged 对象指定模式和填充以匹配 PowerShell 脚本(我知道 CBC 模式是最安全的,但如果其他工作更好,我会更改它(。

任何帮助或指示将不胜感激。

测试代码(.Net Framework 4.7(:

// Note: values are hardcoded based on the results from the PowerShell script
var keyString = "8CBaNtMYwAuu2K/xleoRfgPkURaLK82QidlIyg+nFY4=";
var keyBytes = Convert.FromBase64String(keyString.PadLeft(32)); // Not so sure about this
var ivBytes = Convert.FromBase64String(keyString.PadLeft(16)); // Not so sure about this
var encryptedString = "JW9CDowP0tRGr0Xi7vLxxXv0+fvMzQzopQucLOaeU7s=";
var encryptedByteArray = Convert.FromBase64String(encryptedString);
var test = Cryptography.DecryptStringFromBytes_Aes(encryptedByteArray, keyBytes, ivBytes);

解密方法(不起作用(:

namespace Test.Security
{
using System;
using System.IO;
using System.Security.Cryptography;
public class Cryptography
{
// Note: this method is a Microsoft example, adding PasswordDeriveBytes and specific settings
// https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aesmanaged?view=netframework-4.7.2
public static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] key, byte[] iv)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("Key");
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException("IV");
string plaintext = null;
using (AesManaged aesAlg = new AesManaged())
{
var passwordDerivedbytes = new PasswordDeriveBytes(key, iv);
aesAlg.Key = passwordDerivedbytes.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = passwordDerivedbytes.GetBytes(aesAlg.BlockSize / 8);
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.KeySize = 256;
aesAlg.BlockSize = 128;
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
}

PowerShell 脚本 (PowerShell_AES_Encryption_Example.ps1(:

function Create-AesManagedObject($key, $IV) {
$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
if ($IV) {
if ($IV.getType().Name -eq "String") {
$aesManaged.IV = [System.Convert]::FromBase64String($IV)
}
else {
$aesManaged.IV = $IV
}
}
if ($key) {
if ($key.getType().Name -eq "String") {
$aesManaged.Key = [System.Convert]::FromBase64String($key)
}
else {
$aesManaged.Key = $key
}
}
$aesManaged
}
function Create-AesKey() {
$aesManaged = Create-AesManagedObject
$aesManaged.GenerateKey()
[System.Convert]::ToBase64String($aesManaged.Key)
}
function Encrypt-String($key, $unencryptedString) {
$bytes = [System.Text.Encoding]::UTF8.GetBytes($unencryptedString)
$aesManaged = Create-AesManagedObject $key
$encryptor = $aesManaged.CreateEncryptor()
$encryptedData = $encryptor.TransformFinalBlock($bytes, 0, $bytes.Length);
[byte[]] $fullData = $aesManaged.IV + $encryptedData
$aesManaged.Dispose()
[System.Convert]::ToBase64String($fullData)
}
function Decrypt-String($key, $encryptedStringWithIV) {
$bytes = [System.Convert]::FromBase64String($encryptedStringWithIV)
$IV = $bytes[0..15]
$aesManaged = Create-AesManagedObject $key $IV
$decryptor = $aesManaged.CreateDecryptor();
$unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16);
$aesManaged.Dispose()
[System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)
}

cls
<#
# This will generate a new valid AES 256 key if needed:
# $key = Create-AesKey
#>
<# 
# This is the hard coded key
#>
$key = "8CBaNtMYwAuu2K/xleoRfgPkURaLK82QidlIyg+nFY4="
Write-Host "key = $key"
$unencryptedString = "blahblahblah"
Write-Host "unencryptedString = $unencryptedString"
$encryptedString = Encrypt-String $key $unencryptedString
Write-Host "encryptedString  = $encryptedString "
$backToPlainText = Decrypt-String $key $encryptedString
Write-Host "backToPlainText = $backToPlainText"

<#
# To run this PowerShell script:
#
# In Windows PowerShell:
# .PowerShell_AES_Encryption_Example.ps1
# C:TestPowerShell_AES_Encryption_Example.ps1
# 
# In Command Prompt:
# powershell -noexit "& ""C:TestPowerShell_AES_Encryption_Example.ps1"""
#>

在 PowerShell 脚本中,从消息中提取 IV,但在 C# 中尝试从密钥派生 IV。修复该:)

var keyString = "8CBaNtMYwAuu2K/xleoRfgPkURaLK82QidlIyg+nFY4=";
var keyBytes = Convert.FromBase64String(keyString);
var encryptedString = "JW9CDowP0tRGr0Xi7vLxxXv0+fvMzQzopQucLOaeU7s=";
var encryptedByteArray = Convert.FromBase64String(encryptedString);
// the resulting byte[] will consist of [16 bytes of iv] + [x bytes of encrypted data]
// extract IV
var ivBytes = new byte[16]; 
Array.Copy(encryptedByteArray, ivBytes, ivBytes.Length);
// extract message
var encryptedMessage = new byte[encryptedByteArray.Length - ivBytes.Length];
Array.Copy(encryptedByteArray, ivBytes.Length, encryptedMessage, 0, encryptedMessage.Length);
var test = Cryptography.DecryptStringFromBytes_Aes(encryptedMessage, keyBytes, ivBytes);

我发现我缺少一些东西,对于加密 - 在加密和组合 iv(盐(和加密数据时创建一个随机 IV(盐(,对于解密 - 根据加密字符串创建 IV(盐(并在最后修剪零(因为我使用的是 PaddingMode.Zeros(。还发现了一些有用的例子:mark-adams/aes_example.cs

现在它适用于PowerShell脚本创建的加密值,因此,我最终编写了一个包装类,如果您发现任何错误或任何改进建议,请告诉我:

测试代码

var keyString = "8CBaNtMYwAuu2K/xleoRfgPkURaLK82QidlIyg+nFY4=";
var keyBytes = Convert.FromBase64String(keyString);
var encryptedValue = "JW9CDowP0tRGr0Xi7vLxxXv0+fvMzQzopQucLOaeU7s=";
var testValue = AesWrapper.Decrypt(encryptedValue, keyBytes);
var testEncrypted = AesWrapper.Encrypt(@"Test String", keyBytes);
var testDecrypted = AesWrapper.Decrypt(testEncrypted, keyBytes);
var testReEncrypted = AesWrapper.Encrypt(testEncrypted, keyBytes);

AesWrapper

namespace Yovav.Security
{
using System;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
/// <summary>
/// AES wrapper implementation by Yovav Gad using the AesManaged algorithm.
/// <para>http://en.wikipedia.org/wiki/Advanced_Encryption_Standard</para>
/// </summary>
public sealed class AesWrapper
{
/// <summary>
/// Create a SymmetricAlgorithm using AesManaged
/// </summary>
/// <param name="key">Byte array representing the key values, please note, 
/// for better performance, use Convert.FromBase64String() outside of this method.</param>
/// <param name="blockSize">BlockSize, default is 128</param>
/// <param name="paddingMode">PaddingMode, default is PaddingMode.Zeros</param>
/// <param name="cipherMode">CipherMode, default is CipherMode.CBC</param>
/// <returns></returns>
private static SymmetricAlgorithm CreateCrypto(
byte[] key, 
int blockSize = 128,
PaddingMode paddingMode = PaddingMode.Zeros,
CipherMode cipherMode = CipherMode.CBC
)
{
SymmetricAlgorithm crypto = new AesManaged
{
Key = key,
Mode = cipherMode,
Padding = paddingMode,
BlockSize = blockSize
};
crypto.IV = new byte[crypto.IV.Length];
return (crypto);
}
/// <summary>
/// Decrypt an encrypted string using a specific key.
/// </summary>
/// <param name="str">String to decrypt</param>
/// <param name="key">Byte array representing the key values, please note, 
/// for better performance, use Convert.FromBase64String() outside of this method.</param>
/// <param name="blockSize">BlockSize, default is 128</param>
/// <param name="paddingMode">PaddingMode, default is PaddingMode.Zeros</param>
/// <param name="cipherMode">CipherMode, default is CipherMode.CBC</param>
/// <returns></returns>
[DebuggerStepThrough()]
public static string Decrypt(
string str, 
byte[] key,
int blockSize = 128,
PaddingMode paddingMode = PaddingMode.Zeros,
CipherMode cipherMode = CipherMode.CBC
)
{
if (str == null || str.Length < 1 ||
key == null || key.Length < 1)
{
return null;
}
var result = string.Empty;
using (var crypto = CreateCrypto(key, blockSize, paddingMode, cipherMode))
{
var strCombined = Convert.FromBase64String(str);
var iv = new byte[crypto.BlockSize / 8];
var cipherText = new byte[strCombined.Length - iv.Length];
Array.Copy(strCombined, iv, iv.Length);
Array.Copy(strCombined, iv.Length, cipherText, 0, cipherText.Length);
crypto.IV = iv;
ICryptoTransform decryptor = crypto.CreateDecryptor(key, iv);
using (var msDecrypt = new MemoryStream(cipherText))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
result = srDecrypt.ReadToEnd();
}
}
}
if (paddingMode == PaddingMode.Zeros)
{
// This is required when using PaddingMode.Zeros for values shorted than the block size.
// Note: using .TrimEnd('') to remove nulls and not .TrimEnd("") to allow the string values.
result = result.TrimEnd('');
}
return (result);
}
}
/// <summary>
/// Encrypt a string using a specific key.
/// </summary>
/// <param name="str">String to encrypt</param>
/// <param name="key">Byte array representing the key values, please note, 
/// for better performance, use Convert.FromBase64String() outside of this method.</param>
/// <param name="blockSize">BlockSize, default is 128</param>
/// <param name="paddingMode">PaddingMode, default is PaddingMode.Zeros</param>
/// <param name="cipherMode">CipherMode, default is CipherMode.CBC</param>
/// <returns></returns>
[DebuggerStepThrough()]
public static string Encrypt(
string str,
byte[] key,
int blockSize = 128,
PaddingMode paddingMode = PaddingMode.Zeros,
CipherMode cipherMode = CipherMode.CBC
)
{
if (str == null || str.Length < 1 ||
key == null || key.Length < 1)
{
return null;
}
byte[] encryptedData;
using (SymmetricAlgorithm crypto = CreateCrypto(key, blockSize, paddingMode, cipherMode))
{
byte[] data;
crypto.GenerateIV();
var iv = crypto.IV;
var encryptor = crypto.CreateEncryptor(key, iv);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(str);
}
data = ms.ToArray();
}
}
// Combine the iv (salt) and the encrypted data
encryptedData = new byte[iv.Length + data.Length];
Array.Copy(iv, 0, encryptedData, 0, iv.Length);
Array.Copy(data, 0, encryptedData, iv.Length, data.Length);
}
return Convert.ToBase64String(encryptedData);
}
}
}

最新更新