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

C# 使用特定密钥的AES解密不起作用,c#,.net,powershell,encryption,aes,C#,.net,Powershell,Encryption,Aes,我有一个PowerShell脚本(如下),它使用特定密钥(作为字符串)成功加密和解密字符串值 现在我正在尝试编写一个C#方法,它可以使用相同的密钥对值进行解密,但我不确定如何正确使用PasswordDeriveBytes 此外,我还试图为AES托管对象指定模式和填充,以匹配PowerShell脚本(我知道CBC模式是最安全的,但如果其他方式工作得更好,我会更改它) 任何帮助或指导都将不胜感激 测试代码(.Net Framework 4.7): // Note: values are hardco

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

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

此外,我还试图为AES托管对象指定模式和填充,以匹配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://docs.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;
        }
    }
}
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:\Test\PowerShell_AES_Encryption_Example.ps1
# 
# In Command Prompt:
# powershell -noexit "& ""C:\Test\PowerShell_AES_Encryption_Example.ps1"""
#>
解密方法(不工作):

// 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://docs.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;
        }
    }
}
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:\Test\PowerShell_AES_Encryption_Example.ps1
# 
# In Command Prompt:
# powershell -noexit "& ""C:\Test\PowerShell_AES_Encryption_Example.ps1"""
#>
namespace Test.Security
{
使用制度;
使用System.IO;
使用System.Security.Cryptography;
公共类密码学
{
//注意:此方法是Microsoft示例,添加PasswordDeriveBytes和特定设置
// https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.aesmanaged?view=netframework-4.7.2
公共静态字符串解密StringFromBytes_Aes(字节[]密文,字节[]密钥,字节[]iv)
{
//检查参数。

如果在PowerShell脚本中(cipherText==null | | cipherText.Length,则从消息中提取IV,但在C中,尝试从键派生IV。修复此问题:)

var keyString=“8cbantmywauuu2k/xleoRfgPkURaLK82QidlIyg+nFY4=”;
var keyBytes=Convert.FromBase64String(键串);
var encryptedString=“JW9CDowP0tRGr0Xi7vLxxXv0+fvMzQzopQucLOaeU7s=”;
var encryptedByteArray=Convert.FromBase64String(encryptedString);
//结果字节[]将由[16字节的iv]+[x字节的加密数据]组成
//摘录四
var ivBytes=新字节[16];
复制(encryptedByteArray、ivBytes、ivBytes.Length);
//提取消息
var encryptedMessage=new byte[encryptedByteArray.Length-ivBytes.Length];
Copy(encryptedByteArray,ivBytes.Length,encryptedMessage,0,encryptedMessage.Length);
var test=Cryptography.DecryptStringFromBytes_Aes(encryptedMessage,keyBytes,ivBytes);

我发现我遗漏了一些东西,对于加密-在加密和组合IV(salt)和加密数据时创建一个随机IV(salt),对于解密-基于加密字符串创建IV(salt),并在末尾修剪零(因为我使用PaddingMode.zeros)。还发现了一些有用的示例:

现在它可以使用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);
aeswapper

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('\0') to remove nulls and not .TrimEnd("\0") to allow the string values.
                        result = result.TrimEnd('\0');
                    }

                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);
        }
    }
}
名称空间Yovav.Security
{
使用制度;
使用系统诊断;
使用System.IO;
使用System.Security.Cryptography;
/// 
///由Yovav Gad使用AES管理算法实现AES包装器。
/// http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
/// 
公共密封类包装器
{
/// 
///使用AES创建对称算法
/// 
///表示键值的字节数组,请注意,
///为了获得更好的性能,请在此方法之外使用Convert.FromBase64String()。
///块大小,默认值为128
///PaddingMode,默认为PaddingMode.Zeros
///CipherMode,默认为CipherMode.CBC
/// 
私有静态对称算法CreateCrypto(
字节[]键,
int blockSize=128,
PaddingMode PaddingMode=PaddingMode.zero,
CipherMode CipherMode=CipherMode.CBC
)
{
对称算法加密=新算法
{
键=键,
模式=密码模式,
填充=填充模式,
块大小=块大小
};
crypto.IV=新字节[crypto.IV.Length];
返回(加密);
}
/// 
///使用特定密钥解密加密字符串。
/// 
///要解密的字符串
///表示键值的字节数组,请注意,
///为了获得更好的性能,请在此方法之外使用Convert.FromBase64String()。
///块大小,默认值为128
///PaddingMode,默认为PaddingMode.Zeros
///CipherMode,默认为CipherMode.CBC
/// 
[DebuggerStepThrough()]
公共静态字符串解密(
字符串str,
字节[]键,
int blockSize=128,
PaddingMode PaddingMode=PaddingMode.zero,
CipherMode CipherMode=CipherMode.CBC
)
{
如果(str==null | | str.长度<1||
key==null | | key.长度<1)
{
返回null;
}
var result=string.Empty;
使用(var crypto=CreateCrypto(密钥、块大小、填充模式、密码模式))
{
var strCombined=Convert.FromBase64String(str);
var iv=新字节[crypto.BlockSize/8];
var cipherText=新字节[strCombined.Length-iv.Length];
数组.Copy(strCombined,iv,iv.Length);
复制(strCombined,iv.长度,密文,0,密文.长度);
crypto.IV=IV;
ICryptoTransform decryptor=crypto.CreateDecryptor(密钥,iv);
使用(var msDecrypt=新内存流(密文))
{
使用(var csDecrypt=new CryptoStream(msDecrypt,decryptor,CryptoStreamMode.Read))
{
使用(var srDecrypt=newstreamreader(csDecrypt))
{
结果=srDecrypt.ReadToEnd();
}
}
}
如果(paddingMode==paddingMode