解密用PHP openssl加密的C#中的字符串

解密用PHP openssl加密的C#中的字符串,c#,php,encryption,openssl,C#,Php,Encryption,Openssl,我有一位客户使用以下代码在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($texteACry

我有一位客户使用以下代码在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/w75zuagumesp34ywsyisis9fj0w3q/lR0hBrHmdvMOt106PlKhN/1zXFBPbyKmI6nWC5BN54GuGFSjkxfuansJkfoi0=

当我试图用C#解密这个字符串时,它给了我一堆垃圾,就像这样:
Z�o�}'*2.��I4y�J6S��
��xz���{9^�预计起飞时间�fF
�}��گs�)�Q���我��$)�

我尝试过更改填充,使用AesManaged而不是RijndaelManaged,更改键大小,使用不同的键等。所有这些都会导致不同的垃圾字符串或各种异常。我一定缺少一些非常基本的内容,但我不确定在这一点上还可以尝试什么

这是我的解密代码(我无耻地从另一个stackoverflow问题复制了它:)

类程序
{
//https://stackoverflow.com/questions/5452422/openssl-using-only-net-classes
静态void Main(字符串[]参数)
{
var secret=“Ty63rs4aVqcnh2vUqRJTbNT26caRZJ”;
var encrypted=“MzVWX4tH4yZWc/w75zuagumesp34ywsyisis9fj0w3q/lR0hBrHmdvMOt106PlKhN/1zXFBPbyKmI6nWC5BN54GuGFSjkxfuansJkfoi0=”;
var yeah=OpenSSLDecrypt(加密的,秘密的);
控制台。WriteLine(是的);
Console.ReadKey();
}
公共静态字符串OpenSSLDecrypt(字符串加密,字符串密码)
{
//基64解码
字节[]EncryptedBytesWithAlt=Convert.FromBase64String(加密);
//提取盐(加密的前8字节)
字节[]salt=新字节[8];
byte[]encryptedBytes=新字节[encryptedBytesWithSalt.Length-salt.Length-8];
Buffer.BlockCopy(encryptedBytesWithSalt,8,salt,0,salt.Length);
Buffer.BlockCopy(encryptedBytesWithSalt,salt.Length+8,encryptedBytes,0,encryptedBytes.Length);
//拿到钥匙和iv
字节[]键,iv;
DeriveKeyAndIV(密码短语、salt、out key、out iv);
返回DecryptedStringFromByteSAES(encryptedBytes,key,iv);
}
私有静态void DeriveKeyAndIV(字符串密码短语,字节[]salt,字节[]key,字节[]iv)
{
//生成密钥和iv
列表串联哈希=新列表(48);
byte[]password=Encoding.UTF8.GetBytes(密码短语);
字节[]currentHash=新字节[0];
MD5 MD5=MD5.Create();
bool-enoughBytesForKey=false;
//看http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM
而(!enoughBytesForKey)
{
int preHashLength=currentHash.Length+password.Length+salt.Length;
byte[]preHash=新字节[preHashLength];
BlockCopy(currentHash,0,preHash,0,currentHash.Length);
Buffer.BlockCopy(密码,0,预哈希,currentHash.Length,密码.Length);
BlockCopy(salt,0,preHash,currentHash.Length+password.Length,salt.Length);
currentHash=md5.ComputeHash(preHash);
concatenatedHashes.AddRange(currentHash);
if(concatenatedHashes.Count>=48)
enoughBytesForKey=真;
}
key=新字节[32];
iv=新字节[16];
CopyTo(0,key,0,32);
CopyTo(32,iv,0,16);
md5.Clear();
}
静态字符串解密StringFromByteSAES(字节[]密文,字节[]密钥,字节[]iv)
{
//检查参数。

如果(cipherText==null | | cipherText.Length那么这是一个很有趣的工作,需要跳入PHP源代码并获得一些有趣的结果。首先,PHP甚至没有使用密钥派生算法将其转换为所需的长度。这意味着不需要整个DeriveKeyAndIV方法

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

最后,代码的另一个错误是,您复制它的源代码在其加密实现中使用了salt,然后必须删除该salt,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);
}

ETX是chr(3)。结尾出现3个ETX字符正是PKCS填充的结果:谢谢。我很难弄清楚为什么.NET和PHP使用16字节的密钥获得不同的AES-256密文。I(错误)假设.NET和PHP使用相同的KDF,但从未想过PHP只会对密钥进行零填充。我的数据被详细解密?如何修复?@AlexandrSargsyan我会检查您正在使用的PHP版本是如何派生密钥的,因为这可能已经发生了变化。@DavidEwen我修复了它。问题在于iv密钥。PHP用iv对其进行了加密。而在C#使用iv而不是空字节
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);
}
new RijndaelManaged { Padding = PaddingMode.PKCS7 };