使用VB.net解密AES256加密字符串

I want to decrypt in VB.net strings encrypted in PHP via openssl_encrypt function:

$encrypted_string = openssl_encrypt(
    $string,
    'AES256',
    'secret_password',
    0,
    'initialization_vector'
);

I tried with this class:

Public Class Aes256Encrypter
    Public Function Encrypt(ByVal plainText As String, ByVal secretKey As String) As Byte()
        Dim encryptedPassword As Byte()
        Using outputStream As MemoryStream = New MemoryStream()
            Dim algorithm As RijndaelManaged = getAlgorithm(secretKey)
            Using cryptoStream As CryptoStream = New CryptoStream(outputStream, algorithm.CreateEncryptor(), CryptoStreamMode.Write)
                Dim inputBuffer() As Byte = Encoding.Unicode.GetBytes(plainText)
                cryptoStream.Write(inputBuffer, 0, inputBuffer.Length)
                cryptoStream.FlushFinalBlock()
                encryptedPassword = outputStream.ToArray()
            End Using
        End Using
        Return encryptedPassword
    End Function

    Public Function Decrypt(ByVal encryptedBytes As Byte(), ByVal secretKey As String) As String
        Dim plainText As String = Nothing
        Using inputStream As MemoryStream = New MemoryStream(encryptedBytes)
            Dim algorithm As RijndaelManaged = getAlgorithm(secretKey)
            Using cryptoStream As CryptoStream = New    CryptoStream(inputStream, algorithm.CreateDecryptor(), CryptoStreamMode.Read)
                Dim outputBuffer(0 To CType(inputStream.Length - 1, Integer)) As Byte
                Dim readBytes As Integer = cryptoStream.Read(outputBuffer, 0, CType(inputStream.Length, Integer))
                plainText = Encoding.Unicode.GetString(outputBuffer, 0, readBytes)
            End Using
        End Using
        Return plainText
    End Function

    Private Function getAlgorithm(ByVal secretKey As String) As RijndaelManaged
        Const salt As String = "put your salt here"
        Const keySize As Integer = 256

        Dim keyBuilder As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(secretKey, Encoding.Unicode.GetBytes(salt))
        Dim algorithm As RijndaelManaged = New RijndaelManaged()
        algorithm.KeySize = keySize
        algorithm.IV = keyBuilder.GetBytes(CType(algorithm.BlockSize / 8, Integer))
        algorithm.Key = keyBuilder.GetBytes(CType(algorithm.KeySize / 8, Integer))
        algorithm.Padding = PaddingMode.PKCS7
        Return algorithm
    End Function
End Class

I use IV "initialization_vector" as salt string, but at runtime an error is thrown: 'System.Security.Cryptography.CryptographicException' in mscorlib.dll. An error occurred: Padding is invalid and cannot be removed.

Where am I doing wrong?

doulan1866
doulan1866 我不认为问题是密码长度,因为它比字符串更长(我在15个字符串上使用60个字符密码)。我正在尝试进一步检查.net中的字符串位长,正如jww所建议的那样
3 年多之前 回复
dongquan6030
dongquan6030 您应该将其分为两个问题:第一个是派生,第二个是加密(和解密)。首先应确保将字符串“secret_password”导出到PHP和.Net中的相同字节。.Net对String类使用16位字符串。我猜这是你的第一个问题。
3 年多之前 回复
dttnb997315
dttnb997315 我看到了,但它位于“备注”下,这不是正确的位置,正确的位置在BlockSize参数上。参数声明“(继承自SymmetricAlgorithm。)”这没有帮助。MSDN可以做得更好。
3 年多之前 回复
douzouchang7448
douzouchang7448 msdn.microsoft.com/en-us/library/...,msdn.microsoft.com/en-us/library/...
3 年多之前 回复
dongmiao4733
dongmiao4733 确实很糟糕,默认的块大小不在文档中,需要查看“参考源”。
3 年多之前 回复
doubi7739
doubi7739 在PaddingMode.None下,您可以检查未填充数据的外观。如果你有超过16个字节的解密数据,那么前16个字节中的错误只意味着你有一个IV重建问题,超出这个意外的错误意味着你有一个密钥重建问题。PHP将零填充(在右侧)“太小”的键(来自算法的默认密钥大小)否则使用$password变量作为键。PBKDF2根本不参与。这里的难点在于弄清楚PHP已经解析了什么(如果有的话)。
3 年多之前 回复
dongyanggan3025
dongyanggan3025 你是对的,例子IV是22字节长,但我在实际代码中使用的那个是16字节长(否则它会在PHP加密中引发错误)。我现在尝试指定algorithm.Mode=CipherMode.CBC,但仍然没有正确的解密。使用PaddingMode.PKCS7仍然抛出“填充无效且无法删除”错误。
3 年多之前 回复
duanran3115
duanran3115 在PHP中,IV应该是盐的十六进制表示。我很确定你需要将它转换回VB的文本才能正确使用它
3 年多之前 回复
dongnai6973
dongnai6973 是的,我将其更改为我的初始化向量(在示例中它是“initialization_vector”
3 年多之前 回复
dongyun7897
dongyun7897 你有没有将Constsalt更改为String=“把你的盐放在这里”到你的实际盐值?
3 年多之前 回复
duanjizhan9353
duanjizhan9353 尝试使用PaddingMode.Zeros,它不再抛出错误,但解密的字符串完全错误。我确定我使用的密码和参数与PHP相同
3 年多之前 回复
doukefu1361
doukefu1361 也许试试padmode.zeroes
3 年多之前 回复
douyo770657
douyo770657 基于此:stackoverflow.com/questions/23406135/...它看起来像盐不匹配
3 年多之前 回复
dourong9253
dourong9253 如果“salt”是指初始化向量,则它是字母数字,没有空格。我不知道openssl_encrypt函数默认使用什么类型的填充
3 年多之前 回复
dqnqpqv3841
dqnqpqv3841 盐填充空间?
3 年多之前 回复
dongzaijiao4863
dongzaijiao4863 对不起@SaggingRufus但仍然没有。目前尚不清楚我应该如何使用初始化向量。如果我把它作为一个盐,在运行时它会抛出一个异常,mscorlib.dll中的'System.Security.Cryptography.CryptographicException':填充无效,无法删除
3 年多之前 回复
douwei1128
douwei1128 谢谢,我现在正在尝试这个。
3 年多之前 回复
dtmjl4427
dtmjl4427 可能与VB.NET的AES加密标准库重复?
3 年多之前 回复

1个回答

Okay, so this is a C# answer, and I don't like it; but I've produced a thing which gives the same answer as php's openssl_encrypt.

  • PHP passes the string as if it were bytes, so Encoding.ASCII (not sure what it does with non-ASCII data, actually)
  • PHP/OpenSSL silently truncates long keys (so your 60 character password is a 32 character password)
  • Working with the same data in ASCII strings and hex bytes is a pain in the rear.

Code:

private static byte[] php_encode(string php_string, int expectedLength)
{
    byte[] temp = Encoding.ASCII.GetBytes(php_string);

    if (temp.Length == expectedLength)
    {
        return temp;
    }

    byte[] ret = new byte[expectedLength];
    Buffer.BlockCopy(temp, 0, ret, 0, Math.Min(temp.Length, expectedLength));
    return ret;
}

private static string php_encrypt_aes256(string php_data, string php_password, string php_iv)
{
    byte[] key = php_encode(php_password, 32);
    byte[] iv = php_encode(php_iv, 16);
    byte[] data = Encoding.ASCII.GetBytes(php_data);

    using (Aes aes = Aes.Create())
    using (ICryptoTransform encryptor = aes.CreateEncryptor(key, iv))
    {
        return Convert.ToBase64String(encryptor.TransformFinalBlock(data, 0, data.Length));
    }
}

Test inputs:

PHP:

$ /usr/bin/php -r "print(openssl_encrypt('potato', 'AES256', '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 0, '0123456789ABCDEF') . \"
\");"
wiZGvfABfairN4gKcnqD2Q==
$ /usr/bin/php -r "print(openssl_encrypt('potato', 'AES256', '0123456789abcdefghijklmnopqrstuv', 0, '0123456789ABCDEF') . \"
\");"
wiZGvfABfairN4gKcnqD2Q==
$ /usr/bin/php -r "print(openssl_encrypt('potato', 'AES256', '0123456789abcdefghijklmnopqrstu', 0, '0123456789ABCDEF') . \"
\");"
XUkYwaomkIXYstlOQlNoBQ==
$ /usr/bin/php -r "print(openssl_encrypt('potato', 'AES256', '0123456789abcdefghijklmnopqrstuv', 0, '0123456789ABCDE') . \"
\");"
PHP Warning:  openssl_encrypt(): IV passed is only 15 bytes long, cipher expects an IV of precisely 16 bytes, padding with \0 in Command line code on line 1
ah+yXvQEocgLNES2nh2kXA==

openssl enc

$ openssl enc -aes-256-cbc -K 303132333435363738396162636465666768696a6b6c6d6e6f70717273747576 -in enc.data -a -iv 30313233343536373839414243444546
wiZGvfABfairN4gKcnqD2Q==
$ openssl enc -aes-256-cbc -K 303132333435363738396162636465666768696a6b6c6d6e6f70717273747500 -in enc.data -a -iv 30313233343536373839414243444546
XUkYwaomkIXYstlOQlNoBQ==
$ openssl enc -aes-256-cbc -K 303132333435363738396162636465666768696a6b6c6d6e6f70717273747576 -in enc.data -a -iv 30313233343536373839414243444500
ah+yXvQEocgLNES2nh2kXA==

My code

Console.WriteLine(php_encrypt_aes256("potato", "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789ABCDEF"));
Console.WriteLine(php_encrypt_aes256("potato", "0123456789abcdefghijklmnopqrstuv", "0123456789ABCDEF"));
Console.WriteLine(php_encrypt_aes256("potato", "0123456789abcdefghijklmnopqrstu", "0123456789ABCDEF"));
Console.WriteLine(php_encrypt_aes256("potato", "0123456789abcdefghijklmnopqrstuv", "0123456789ABCDE"));

Output:

wiZGvfABfairN4gKcnqD2Q==
wiZGvfABfairN4gKcnqD2Q==
XUkYwaomkIXYstlOQlNoBQ==
ah+yXvQEocgLNES2nh2kXA==
dongnius85154
dongnius85154 它工作得很好,谢谢!
3 年多之前 回复
drau67562
drau67562 反转操作:Convert.FromBase64String将字符串转换为数据,然后使用CreateDecryptor而不是CreateEncryptor,并将Encoding.ASCII.GetString的字节恢复为原始字符串。 相同的密钥/ IV处理代码。
3 年多之前 回复
duandan1995
duandan1995 太好了,这很好用! 我得到的结果与openssl_encrypt相同。 但为了实现逆向操作,我该怎么办?
3 年多之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐