将mcrypt MCRYPT_RIJNDAEL_256替换为openssl或将其更改为AES

时间:2017-02-06 18:53:17

标签: c# php encryption openssl

我正在构建一个简单的PHP脚本,需要解码来自C#应用程序的输入 我用以下加密函数创建了C#app(我还包括了我的解密函数):

public static string Encrypt(string input, string key)
{
    var aes = new RijndaelManaged
    {
        KeySize = 256,
        BlockSize = 256,
        Padding = PaddingMode.PKCS7,
        Mode = CipherMode.CBC,
        Key = Encoding.UTF8.GetBytes(key) 
    };

    aes.GenerateIV();
    var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
    byte[] buffer;
    using (var ms = new MemoryStream())
    {
        using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        {
            byte[] bytes = Encoding.UTF8.GetBytes(input);
            cs.Write(bytes, 0, bytes.Length);
        }
        buffer = ms.ToArray();
    }
    buffer = buffer.Concat(aes.IV).ToArray();
    return Convert.ToBase64String(buffer);
}

private static String Decrypt(string text, string key)
{
    RijndaelManaged aes = new RijndaelManaged
    {
        KeySize = 256,
        BlockSize = 256,
        Mode = CipherMode.CBC,
        Padding = PaddingMode.PKCS7,
        Key = Encoding.UTF8.GetBytes(key)
    };

    byte[] encoded = Convert.FromBase64String(text);
    byte[] buffer = encoded.Take(encoded.Length - aes.IV.Length).ToArray();
    aes.IV = encoded.Skip(encoded.Length - aes.IV.Length).ToArray();
    var decrypt = aes.CreateDecryptor();
    byte[] xBuff;
    using (var ms = new MemoryStream())
    {
        using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
        {
            cs.Write(buffer, 0, buffer.Length);
        }
        xBuff = ms.ToArray();
    }
    var output = Encoding.UTF8.GetString(xBuff);
    return output;
}

经过几分钟的搜索,我发现PHP中的简单解密功能使用mcrypt:

function strippadding($string)
{
    $slast = ord(substr($string, -1));
    $slastc = chr($slast);
    $pcheck = substr($string, -$slast);
    if(preg_match("/$slastc{".$slast."}/", $string)){
    $string = substr($string, 0, strlen($string)-$slast);
        return $string;
    } else {
        return false;
    }
}

function decrypt($string, $key)
{
    $string = base64_decode($string);
    $iv = substr($string, -32);
    $string = str_replace($iv, "", $string);
    return strippadding(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $string, MCRYPT_MODE_CBC, $iv));
}

这很好用,但是当我在多个网站上阅读时,不再推荐使用mcrypt,迟早会删除它。

我正在尝试使用openssl重新创建相同的功能,但没有任何运气。 我已尝试将mcrypt_decrypt替换为:

openssl_decrypt($string, 'aes-256-cbc', $encryption_key, 0, $iv);

但是我发现了MCRYPT_RIJNDAEL_256 doesn't mean AES-256. 我一直在尝试使用不同的密钥大小和块大小,但没有运气。

如何使用openssl重新创建PHP解密函数?

EDIT1:
我在C#代码中使用RijndaelManaged更改了AesCryptoServiceProvider

var aes = new AesCryptoServiceProvider()
{
    KeySize = 256,
    BlockSize = 128,
    Padding = PaddingMode.PKCS7,
    Mode = CipherMode.CBC,
    Key = Encoding.UTF8.GetBytes(key) 
};

并在PHP内部:

define('AES_128_CBC', 'aes-128-cbc');

function decrypt_openssl($string, $pkey)
{
    $key = $pkey;
    $string = base64_decode($string);
    $iv = substr($string, -32);
    $string = str_replace($iv, "", $string);

    $decrypted = openssl_decrypt($string, AES_128_CBC, base64_encode($key), 0, base64_encode($iv));
    return $decrypted;
}

但我仍然无法在PHP中解码编码字符串。

我需要一种方法来解密我的C#函数的输出,或者更改两者以使双向通信正常工作。

EDIT2:
我正在提供我的C#类的完整源代码:

public static string EncryptRijndael(string input, string key)
{
    var aes = new RijndaelManaged
    {
        KeySize = 256,
        BlockSize = 256,
        Padding = PaddingMode.PKCS7,
        Mode = CipherMode.CBC,
        Key = Encoding.UTF8.GetBytes(key)
    };

    aes.GenerateIV();

    var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

    byte[] buffer;
    using (var ms = new MemoryStream())
    {
        using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        {
            byte[] bytes = Encoding.UTF8.GetBytes(input);
            cs.Write(bytes, 0, bytes.Length);
        }
        buffer = ms.ToArray();
    }

    buffer = buffer.Concat(aes.IV).ToArray();

    aes.Dispose();
    return Convert.ToBase64String(buffer);
}

public static string DecryptRijndael(string input, string key)
{
    var aes = new RijndaelManaged
    {
        KeySize = 256,
        BlockSize = 256,
        Mode = CipherMode.CBC,
        Padding = PaddingMode.PKCS7,
        Key = Encoding.UTF8.GetBytes(key)
    };

    byte[] xXml = Convert.FromBase64String(input);
    var buffer = xXml.Take(xXml.Length - aes.IV.Length).ToArray();
    var iv = xXml.Skip(xXml.Length - aes.IV.Length).ToArray();

    aes.IV = iv;
    var decrypt = aes.CreateDecryptor();
    byte[] xBuff;
    using (var ms = new MemoryStream())
    {
        using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
        {
            cs.Write(buffer, 0, buffer.Length);
        }
        xBuff = ms.ToArray();
    }
    aes.Dispose();
    String output = Encoding.UTF8.GetString(xBuff);
    return output;
}


public static string EncryptAes(string input, string key)
{
    var aes = new AesCryptoServiceProvider()
    {
        KeySize = 256,
        BlockSize = 128,
        Padding = PaddingMode.PKCS7,
        Mode = CipherMode.CBC,
        Key = Encoding.UTF8.GetBytes(key) 
    };

    aes.GenerateIV();
    var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
    byte[] buffer;
    using (var ms = new MemoryStream())
    {
        using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        {
            byte[] bytes = Encoding.UTF8.GetBytes(input);
            cs.Write(bytes, 0, bytes.Length);
        }
        buffer = ms.ToArray();
    }

    buffer = buffer.Concat(aes.IV).ToArray();
    aes.Dispose();
    return Convert.ToBase64String(buffer);
}

public static String DecryptAes(string input, string key)
{
    var aes = new AesCryptoServiceProvider()
    {
        KeySize = 256,
        BlockSize = 128,
        Mode = CipherMode.CBC,
        Padding = PaddingMode.PKCS7,
        Key = Encoding.UTF8.GetBytes(key) 
    };

    byte[] xXml = Convert.FromBase64String(input);
    var buffer = xXml.Take(xXml.Length - aes.IV.Length).ToArray();
    var iv = xXml.Skip(xXml.Length - aes.IV.Length).ToArray();

    aes.IV = iv;
    var decrypt = aes.CreateDecryptor();
    byte[] xBuff;
    using (var ms = new MemoryStream())
    {
        using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
        {
            cs.Write(buffer, 0, buffer.Length);
        }
        xBuff = ms.ToArray();
    }
    aes.Dispose();
    String output = Encoding.UTF8.GetString(xBuff);
    return output;
}

我的测试密钥是:zjPUcCp9Jn7k8RtEzxTRePjn984LqwyN
我的测试数据为纯文本:zażółć geślą jaźń
我的测试数据为base64:emHFvMOzxYLEhyBnZcWbbMSFIGphxbrFhA==
两个加密函数的示例输出:
Rijndael :4gD / tt3I3hqYToLnwxI / HJ37EHfXrd1uxchIOjuxSuZl0Kyvxb + S6h4gG3cWKJTbj0wDSH1zvbeSvHd9Wu1VaA ==
AES :B0dKdL4k9J6CeqlAekaXM + eh / zDqd5B4sKK2p6DFsgYNbV56Xdy01XvYPZX8ZXBc

在创建base64输出之前,在字节数组的末尾添加了IV。在解密时我从输入字符串的末尾读取IV并使用它来解密。

我需要确保我可以加密/解密utf-8字符串。

1 个答案:

答案 0 :(得分:0)

由于必要的格式和长度,这不是一个答案,而是作为答案添加:

由OP提供:

加密输入:
测试密钥是:zjPUcCp9Jn7k8RtEzxTRePjn984LqwyN
将数据作为纯文本进行测试:zażółć geślą jaźń(未使用,见下文)
测试数据为base64:emHFvMOzxYLEhyBnZcWbbMSFIGphxbrFhA==(已使用)
两种加密功能的样本输出:
Rijndael:4gD/tt3I3hqYToLnwxI/HJ37EHfXrd1uxchIOjuxSuZl0Kyvxb+S6h4gG3cWKJTbj0wDSH1zvbeSvHd9Wu1VaA==
AES:B0dKdL4k9J6CeqlAekaXM+eh/zDqd5B4sKK2p6DFsgYNbV56Xdy01XvYPZX8ZXBc

提供的数据显示为十六进制以保持一致性,为易读性添加了空格:

Rijndael预计:e200ffb6 ddc8de1a 984e82e7 c3123f1c 9dfb1077 d7addd6e c5c8483a 3bb14ae6 65d0acaf c5bf92ea 1e201b77 162894db 8f4c0348 7d73bdb7 92bc777d 5aed5568
AESExpected:07474a74 be24f49e 827aa940 7a469733 e7a1ff30 ea779078 b0a2b6a7 a0c5b206 0d6d5e7a 5ddcb4d5 7bd83d95 fc65705c

testData:7a61c5bc c3b3c582 c4872067 65c59b6c c485206a 61c5bac5 84
testKey:7a6a5055 63437039 4a6e376b 38527445 7a785452 65506a6e 3938344c 7177794e
testIV:8f4c0348 7d73bdb7 92bc777d 5aed5568(来自RijndaelExpected尾随字节)。

以上是人们在一个问题中所希望的。

假设:PKCS#7填充,CBC模式。
基于数据,预期填充7字节,因此加密数据应为32字节。

RijndaelExpected输出是64字节,减去16字节,附加的IV是48字节,这是不正确的。

AESExpected输出为48字节。如果附加了IV,它与RijndaelExpected不匹配,它没有附加IV它是错误的长度。

我计算出的没有附加IV的AES输出是(注意IV通常是加密数据的前缀):

CryptData:1a6ec05d 00a6e61b 8196e7f2 879e2f59 25d3b7e2 c103f7e6 41c8c93f 70b32de5
这与预期的产出都不一致。

另请参阅此online AES calculator请注意,已手动添加PKCS#7填充。

仅限完整性,我的测试代码:

NSData *testData = [[NSData alloc] initWithBase64EncodedString:@"emHFvMOzxYLEhyBnZcWbbMSFIGphxbrFhA==" options:0];
NSData *testKey  = [@"zjPUcCp9Jn7k8RtEzxTRePjn984LqwyN" dataUsingEncoding:NSUTF8StringEncoding];
NSData *testIV   = [[NSData alloc] initWithBase64EncodedString:@"j0wDSH1zvbeSvHd9Wu1VaA==" options:0];

size_t movedBytes = 0;
NSMutableData *cryptData = [NSMutableData dataWithLength: testData.length + kCCBlockSizeAES128];
CCCrypt(kCCEncrypt, kCCAlgorithmAES128,
        kCCOptionPKCS7Padding, // CBC  mode is the default
        testKey.bytes, kCCKeySizeAES256,
        testIV.bytes,
        testData.bytes, testData.length,
        cryptData.mutableBytes, cryptData.length,
        &movedBytes);
cryptData.length = movedBytes;
Display(@"CryptData: %@", cryptData);