如何在 C# 中解密来自 openssl_encrypt 的字符串

How to decrypt string from openssl_encrypt in c#

这是我的 php openssl_encrypt:

 $text = "does this work";
 $key = "1234567890123456789012345678901234567890123456789012345678901234";
 $method = "AES-256-CBC";
 $cipher_length = openssl_cipher_iv_length($method); //16
 $iv_new = openssl_random_pseudo_bytes($cipher_length);
 $bin2hex = bin2hex($iv_new); //"a64d63874ba9ee8f5a5028cb40ab70a4"
 $openssl = openssl_encrypt($text, $method, $key, 0, $iv_new);
 $encrypted =  $bin2hex . $openssl;

$encrypted 的值为:

"a64d63874ba9ee8f5a5028cb40ab70a4yFFQwLuOHeWouyfp0dyrnw==" //encrypted in DecryptPHP

这是我的 php openssl_decrypt:

$iv_strlen = 32;
preg_match("/^(.{" . $iv_strlen . "})(.+)$/", $encrypted, $regs);
list(, $_iv, $_crypted_string) = $regs;

$_hex2bin = hex2bin($_iv); 
$ctype_xdigit = ctype_xdigit($_iv);
$remainder = strlen($_iv) % 2 == 0;
$new_text =  openssl_decrypt($_crypted_string, $method, $key, 0, $_hex2bin);

$new_text 值为:

"does this work"

所以这工作正常,但我怎样才能得到这个字符串:

 "a64d63874ba9ee8f5a5028cb40ab70a4yFFQwLuOHeWouyfp0dyrnw=="

并在 C# 中将其转换为“这行得通吗”?我已经尝试了很多东西并不断提出错误的数据。我什至没有运气地尝试过这个。任何帮助将不胜感激。

Decrypt string in C# that was encrypted with PHP openssl_encrypt

这是我的 C# 代码:

             string DecryptPHP()
            {
    
                //var _key = System.Text.Encoding.UTF8.GetBytes(key);
                //var _iv =  System.Text.Encoding.UTF8.GetBytes(iv);
                var key = key = "1234567890123456789012345678901234567890123456789012345678901234";
                var _key = FromHex(key);
                var iv = "a64d63874ba9ee8f5a5028cb40ab70a4";
                var _iv = FromHex(iv);

                //pad key out to 32 bytes (256bits) if its too short
                if (_key.Length < 32)
                {
                    var paddedkey = new byte[32];
                    Buffer.BlockCopy(_key, 0, paddedkey, 0, key.Length);
                    _key = paddedkey;
                } 

                //get the encrypted data and decrypt
                var data_encrypted_string = "yFFQwLuOHeWouyfp0dyrnw==";
                byte[] encryptedBytes = Convert.FromBase64String(data_encrypted_string);
                return DecryptStringFromBytesAes(encryptedBytes, _key, _iv);
            }

            byte[] FromHex(string hex)
            {
                hex = hex.Replace("-", "");
                byte[] raw = new byte[hex.Length / 2];
                for (int i = 0; i < raw.Length; i++)
                {
                    raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
                }
                return raw;
            }

            static string DecryptStringFromBytesAes(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");

                // Declare the RijndaelManaged object
                // used to decrypt the data.
                RijndaelManaged aesAlg = null;

                // Declare the string used to hold
                // the decrypted text.
                string plaintext;

                // Create a RijndaelManaged object
                // with the specified key and IV.
                aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.None, KeySize = 256, BlockSize = 128, Key = key, IV = iv };

                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {
                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                            srDecrypt.Close();
                        }
                    }
                }

                return plaintext;
            }

这里有些垃圾我没有清理,请见谅。你有两件事要改变。搜索括号中的文本以查找修复位置。

  • (修复 1)我在某处读到 php 方法会截断比应有的长度更长的键。您的密钥是 64 个字符,但应该是 32 个字符。
  • (修复 2)PaddingMode 看起来应该是 PKCS7

更新

代码保持不变,但我又添加了一段我忘记删除的代码:

  • (修复 3)密钥应该是文本,而不是十六进制。
using System;
using System.Security.Cryptography;
using System.IO;
using System.Linq;
using System.Collections.Generic;
                    // 
//
// https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rijndaelmanaged?view=net-5.0
public class Program
{
    // full key 1234567890123456789012345678901234567890123456789012345678901234
    
    // Fix 1: php truncates a key longer than what is supported, so I chopped this off to 32
    private static string key = "12345678901234567890123456789012";
    private static string originalText = "does this work";
    private static string encryptedValue = "a64d63874ba9ee8f5a5028cb40ab70a4yFFQwLuOHeWouyfp0dyrnw==";
    
    public static void Main()
    {
        // Console.WriteLine(OpenSslDecrypt(encryptedValue, key));
        Console.WriteLine("decrypted text: " + DecryptPHP(encryptedValue));
    }
    
    public static byte[] FromHex(string hex)
    {
        hex = hex.Replace("-", "");
        byte[] raw = new byte[hex.Length / 2];
        for (int i = 0; i < raw.Length; i++)
        {
            raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
        }
        return raw;
    }
    
    public static string DecryptPHP(string encrypted)
    {
        var hexSalt = encryptedValue.Substring(0, 32);
        byte[] stringSalt = FromHex(hexSalt);
        
        var saltBytes = FromHex(hexSalt);
        Console.WriteLine("salt length bytes: " + saltBytes.Length);
        var unsaltedEncryptedValue = encryptedValue.Substring(32);
        
        Console.WriteLine("salt: " + hexSalt);
        Console.WriteLine("unsaltedEncryptedValue: " + unsaltedEncryptedValue);

        //get the encrypted data and decrypt
        byte[] unsaltedEncryptedBytes = Convert.FromBase64String(unsaltedEncryptedValue);
        // byte[] unsaltedEncryptedBytes = System.Text.ASCIIEncoding.UTF8.GetBytes(unsaltedEncryptedValue);

        // Fix 3: key should be text, not hex.
        // var _key = FromHex(key);
        byte[] keyBytes = System.Text.ASCIIEncoding.UTF8.GetBytes(key);
        
        return DecryptStringFromBytesAes(unsaltedEncryptedBytes, keyBytes, saltBytes);
    }

    public static string DecryptStringFromBytesAes(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");

        // Declare the RijndaelManaged object
        // used to decrypt the data.
        RijndaelManaged aesAlg = null;

        // Declare the string used to hold
        // the decrypted text.
        string plaintext;

        // Create a RijndaelManaged object
        // with the specified key and IV.
        //  Fix 2: PaddingMode should be PKCS7, based on my tests. It gets rid of the extra ? chars.
        aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7, KeySize = 256, BlockSize = 128, Key = key, IV = iv };

        // Create a decrytor to perform the stream transform.
        ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
        // Create the streams used for decryption.
        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        {
            using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                {
                    // Read the decrypted bytes from the decrypting stream
                    // and place them in a string.
                    plaintext = srDecrypt.ReadToEnd();
                    srDecrypt.Close();
                }
            }
        }

        return plaintext;
    }
}

dotnetfiddle link: https://dotnetfiddle.net/s2tfmM