我试图在PHP中使用C#和openssl_decrypt重新创建TripleDES ECB的PKCS7实现。
private static string Key = "<some random key with umlauts and special characters length of 24>";
public static string Decrypt(string cypherText)
{
using(var des = CreateDes(Key))
{
var ct = des.CreateDecryptor();
var input = Convert.FromBase64String(cypherText);
var output = ct.TransformFinalBlock(input, 0, input.Length);
return Encoding.UTF8.GetString(output);
}
}
public static string Encrypt(string plainText)
{
using(var des = CreateDes(Key))
{
var ct = des.CreateEncryptor();
var input = Encoding.UTF8.GetBytes(plainText);
var output = ct.TransformFinalBlock(input, 0, input.Length);
return Convert.ToBase64String(output);
}
}
private static TripleDES CreateDes(string key)
{
MD5 md5 = new MD5CryptoServiceProvider();
TripleDES des = new TripleDESCryptoServiceProvider();
var desKey = md5.ComputeHash(Encoding.UTF8.GetBytes(key));
des.Key = desKey;
des.IV = new byte[des.BlockSize / 8];
des.Padding = PaddingMode.PKCS7;
des.Mode = CipherMode.ECB;
return des;
}到目前为止,我已经成功地发现,我必须使用PHP中MD5-函数的raw_output参数来获得完全相同的密钥(与C#中的断点和PHP中的getByteFromString-函数相比),加密/解密基本上是对双方都起作用的。但是,在C#中加密的值不能在PHP中解密,反之亦然,因为加密结果并不相同。
到目前为止,我在PHP中获得的内容如下:
function getByteFromString( $value )
{
$ret = '';
for($i = 0; $i < strlen($value); $i++)
{
$ret .= '[' . $i . '] => ' . ord($value[$i])."<br/>";
}
return $ret;
}
function encrypt( $key, $value )
{
if( function_exists( 'openssl_encrypt' ) )
{
return base64_encode( openssl_encrypt( $value, 'DES-EDE3', $key, OPENSSL_RAW_DATA ) );
}
return 'openssl missing';
}
function decrypt( $key, $value )
{
if( function_exists( 'openssl_decrypt' ) )
{
return openssl_decrypt( base64_decode( $value ), 'DES-EDE3', $key, OPENSSL_RAW_DATA );
}
return 'openssl missing';
}
$sKey = md5("<the same random key with umlauts and special characters length of 24 as in c#>", true);
$number = '1234567890';
$encrypted = encrypt( $sKey, $number );
$decrypted = decrypt( $sKey, $encrypted );
// For key debugging only:
echo 'key:<br>' . getByteFromString($sKey) . '<br>';
echo 'encrypted: ' . var_export($encrypted, true) . '<br>';
echo 'decrypted: ' . var_export($decrypted, true). '<br>';我知道TripleDES不应该再被使用,尤其是欧洲央行模式,但我不能更改C#代码,因此PHP必须创建与C#相同的结果,并且必须能够解密C#加密的值以及加密值,以便C#能够使用TripleDES和ECB解密它们。我只是不知道我在PHP方面缺少了什么。
发布于 2020-03-31 18:16:06
好的,我通过阅读相关的问题找到了解决办法。我在一个有7年历史的问题中发现了这个问题,尽管有人要求它使用不推荐的mcrypt函数,但不知怎么的,它仍然在使用openssl函数。
所要做的就是将原始键的前8个字节附加到其上,如下所示:
$sKey = md5("<the same random key with umlauts and special characters length of 24 as in c#>", true);
$sKey .= substr( $sKey, 0, 8 );它给出了以下PHP中的工作示例:
function encrypt( $key, $value )
{
if( function_exists( 'openssl_encrypt' ) )
{
return base64_encode( openssl_encrypt( $value, 'DES-EDE3', $key, OPENSSL_RAW_DATA ) );
}
return 'openssl missing';
}
function decrypt( $key, $value )
{
if( function_exists( 'openssl_decrypt' ) )
{
return openssl_decrypt( base64_decode( $value ), 'DES-EDE3', $key, OPENSSL_RAW_DATA );
}
return 'openssl missing';
}
$sKey = md5("<the same random key with umlauts and special characters length of 24 as in c#>", true);
$sKey .= substr( $sKey, 0, 8 ); //Added this line to fix it
$number = '1234567890';
$encrypted = encrypt( $sKey, $number );
$decrypted = decrypt( $sKey, $encrypted );
echo 'encrypted: ' . var_export($encrypted, true) . '<br>';
echo 'decrypted: ' . var_export($decrypted, true). '<br>';如果有人可以评论为什么这是解决办法,请这样做。虽然我对它的工作很满意,但我真的很想了解为什么在工作。我不能在C#中看到这样的事情发生,所以为什么我必须在PHP中这样做呢?
发布于 2022-11-30 09:21:20
我认为,需要向sKey添加额外8个字节的原因是,将二进制参数设置为true的MD5()函数返回长度为16的原始二进制格式(按照文档https://www.php.net/manual/en/function.md5.php)。然后,tripleDES需要一个24字节的密钥。在末尾添加前8个字节,加起来将达到24个字节。在这里(https://en.wikipedia.org/wiki/Triple_DES),在“键控选项”下,您可以选择键控选项2-- K1和K2是独立的,K3 = K1。有时被称为2 2TDEA或双长键。(根据维基百科)
https://stackoverflow.com/questions/60956723
复制相似问题