【发布时间】:2017-06-23 19:52:51
【问题描述】:
我正在构建一个简单的 PHP 脚本,它需要对来自 C# 应用程序的输入进行解码。
我创建了具有以下加密功能的 C# 应用程序(我还包括了我的解密功能):
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;
}
我的测试密钥是: IV 在创建 base64 输出之前添加到字节数组的末尾。解密时,我从输入字符串的末尾读取 IV 并使用它来解密。 我需要确保我可以加密/解密 utf-8 字符串。zjPUcCp9Jn7k8RtEzxTRePjn984LqwyN
我的纯文本测试数据:zażółć geślą jaźń
我的测试数据为base64:emHFvMOzxYLEhyBnZcWbbMSFIGphxbrFhA==
和用于加密功能的输出样本:结果的 Rijndael算法强>:4GD / tt3I3hqYToLnwxI / HJ37EHfXrd1uxchIOjuxSuZl0Kyvxb + S6h4gG3cWKJTbj0wDSH1zvbeSvHd9Wu1VaA ==结果的 AES 强>:B0dKdL4k9J6CeqlAekaXM + EH / zDqd5B4sKK2p6DFsgYNbV56Xdy01XvYPZX8ZXBc P>
【问题讨论】:
-
Rijndael 具有 16 字节的块大小和 128、192 或 256 位的密钥大小是 AES。 PHP mcrypt 是个问题,它不支持标准的 PKCS#7 (née PKCS#5) 填充,只支持非标准的空填充。
-
@zaph 我不是 PHP 开发人员,所以如果你能告诉我如何修改我的代码。正如我正确理解的那样,我必须在 PHP 中设置它?还是我必须更改 C# 部分?
-
@zaph 我必须同意 mcrypt 是不可能的,这就是我想使用 openssl 的原因,我可能想避免使用任何外部库。在 C# 部分中,我使用带有 PKCS7 填充的 CBC 模式。
-
@zaph 我可以更改 C# 部分,以便在 PHP 网站上更容易解密。
标签: c# php encryption openssl