繁体   English   中英

解压缩使用PHP openssl_encrypt加密的C#中的字符串

[英]Decrypt string in C# that was encrypted with PHP openssl_encrypt

我有一个客户使用以下代码在PHP中加密字符串:

    $password = 'Ty63rs4aVqcnh2vUqRJTbNT26caRZJ';
    $method = 'AES-256-CBC';
    texteACrypter = 'Whether you think you can, or you think you can\'t--you\'re right. - Henry Ford';

    $encrypted = openssl_encrypt($texteACrypter, $method, $password);

导致此加密输出: MzVWX4tH4yZWc/w75zUagUMEsP34ywSYISsIIS9fj0W3Q/lR0hBrHmdvMOt106PlKhN/1zXFBPbyKmI6nWC5BN54GuGFSjkxfuansJkfoi0=

当我尝试在C#中解密该字符串时,它给了我一堆像这样的垃圾: Z o }'*2 I4y J6S xz {9^ ED fF? } گs ) Q i $) Z o }'*2 I4y J6S xz {9^ ED fF? } گs ) Q i $)

我尝试使用AesManaged而不是RijndaelManaged更改填充,更改密钥大小,使用不同的密钥等。所有这些都会导致不同的垃圾字符串或各种异常。 我必须在这里遗漏一些非常基本的东西但是我不确定在这一点上还有什么可以尝试的。

这是我的解密代码(我无耻地从另一个stackoverflow问题复制: openssl只使用.NET类

class Program
{
    //https://stackoverflow.com/questions/5452422/openssl-using-only-net-classes
    static void Main(string[] args)
    {
        var secret = "Ty63rs4aVqcnh2vUqRJTbNT26caRZJ";
        var encrypted = "MzVWX4tH4yZWc/w75zUagUMEsP34ywSYISsIIS9fj0W3Q/lR0hBrHmdvMOt106PlKhN/1zXFBPbyKmI6nWC5BN54GuGFSjkxfuansJkfoi0=";

        var yeah = OpenSSLDecrypt(encrypted, secret);
        Console.WriteLine(yeah);
        Console.ReadKey();
    }

    public static string OpenSSLDecrypt(string encrypted, string passphrase)
    {
        // base 64 decode
        byte[] encryptedBytesWithSalt = Convert.FromBase64String(encrypted);
        // extract salt (first 8 bytes of encrypted)
        byte[] salt = new byte[8];
        byte[] encryptedBytes = new byte[encryptedBytesWithSalt.Length - salt.Length - 8];
        Buffer.BlockCopy(encryptedBytesWithSalt, 8, salt, 0, salt.Length);
        Buffer.BlockCopy(encryptedBytesWithSalt, salt.Length + 8, encryptedBytes, 0, encryptedBytes.Length);
        // get key and iv
        byte[] key, iv;
        DeriveKeyAndIV(passphrase, salt, out key, out iv);
        return DecryptStringFromBytesAes(encryptedBytes, key, iv);
    }

    private static void DeriveKeyAndIV(string passphrase, byte[] salt, out byte[] key, out byte[] iv)
    {
        // generate key and iv
        List<byte> concatenatedHashes = new List<byte>(48);

        byte[] password = Encoding.UTF8.GetBytes(passphrase);
        byte[] currentHash = new byte[0];
        MD5 md5 = MD5.Create();
        bool enoughBytesForKey = false;
        // See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM
        while (!enoughBytesForKey)
        {
            int preHashLength = currentHash.Length + password.Length + salt.Length;
            byte[] preHash = new byte[preHashLength];

            Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length);
            Buffer.BlockCopy(password, 0, preHash, currentHash.Length, password.Length);
            Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + password.Length, salt.Length);

            currentHash = md5.ComputeHash(preHash);
            concatenatedHashes.AddRange(currentHash);

            if (concatenatedHashes.Count >= 48)
                enoughBytesForKey = true;
        }

        key = new byte[32];
        iv = new byte[16];
        concatenatedHashes.CopyTo(0, key, 0, 32);
        concatenatedHashes.CopyTo(32, iv, 0, 16);

        md5.Clear();
    }

    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;
    }
}

好吧,这很有趣,需要跳过PHP源代码并获得一些有趣的结果。 首先,PHP甚至不使用密钥派生算法,它只需要使用密码短语的字节,然后用零填充它到所需的长度。 这意味着不需要整个DeriveKeyAndIV方法。

由于上述原因,这意味着正在使用的IV是包含零的16长度字节数组。

最后,你的代码唯一的另一个错误是你复制它的源在他们的加密实现中使用了盐,然后必须删除,PHP也没有你这样做,所以删除salt字节是不正确的。

因此,所有这些放在一起意味着您需要将OpenSSLDecrypt方法更改为此。

public static string OpenSSLDecrypt(string encrypted, string passphrase)
{
    //get the key bytes (not sure if UTF8 or ASCII should be used here doesn't matter if no extended chars in passphrase)
    var key = Encoding.UTF8.GetBytes(passphrase);

    //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;
    }

    //setup an empty iv
    var iv = new byte[16];

    //get the encrypted data and decrypt
    byte[] encryptedBytes = Convert.FromBase64String(encrypted);
    return DecryptStringFromBytesAes(encryptedBytes, key, iv);
}

最后,结果字符串在末尾有一些额外的字符,即一组3个ETX字符,但这些应该很容易过滤掉。 我实际上无法弄清楚这些来自哪里。

感谢@neubert指出填充是标准PKCS填充的一部分,如果您希望框架删除它,只需在实例化RijndaelManaged对象时指定为填充模式。

new RijndaelManaged { Padding = PaddingMode.PKCS7 };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM