我正在构建一个简单的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字符串。
答案 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);