【问题标题】:Porting C# TripleDESCryptoServiceProvider encryption to PHP将 C# TripleDESCryptoServiceProvider 加密移植到 PHP
【发布时间】:2013-12-14 12:32:27
【问题描述】:

有一个用 C# 编写的项目,它使用以下代码加密/解密数据库中的数据:

public string EncryptString(string Text)
{
    byte[] IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
    string Key = "abcdef";
    byte[] buffer = Encoding.UTF8.GetBytes(Text);
    TripleDESCryptoServiceProvider triple = new TripleDESCryptoServiceProvider();
    MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
    triple.IV = IV;
    triple.Key = md5.ComputeHash(ASCIIEncoding.UTF8.GetBytes(Key));
    byte[] encodeText = triple.CreateEncryptor().TransformFinalBlock(buffer, 0, buffer.Length);
    string user = Convert.ToBase64String(encodeText);
    return user;
}

public static string DecryptString(string EncryptText)
{
    byte[] IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
    string Key = "abcdef";
    byte[] decodeText = null;
    byte[] buffer = Convert.FromBase64String(EncryptText);
    TripleDESCryptoServiceProvider triple = new TripleDESCryptoServiceProvider();
    MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
    triple.IV = IV;
    triple.Key = md5.ComputeHash(ASCIIEncoding.UTF8.GetBytes(Key));
    decodeText = triple.CreateDecryptor().TransformFinalBlock(buffer, 0, buffer.Length);
    return Encoding.UTF8.GetString(decodeText);
}

现在我应该在 PHP 中做同样的事情。到目前为止,我与以下代码一样接近:

define('CIPHER', 'tripledes');
define('MODE', 'cbc');

function Encrypt($data)
{ 
    $iv = chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8);
    $key = 'abcdef';
    $tripleKey = substr(md5($key), 0, mcrypt_get_key_size(CIPHER, MODE));
    $encodedText = mcrypt_encrypt(CIPHER, $tripleKey, $data, MODE, $iv);
    return base64_encode($encodedText);
}

function Decrypt($data)
{
    $iv = chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8);
    $key = 'abcdef';
    $tripleKey = substr(md5($key), 0, mcrypt_get_key_size(CIPHER, MODE));
    $decodedText = mcrypt_decrypt(CIPHER, $tripleKey, base64_decode($data), MODE, $iv);
    return $decodedText;
}

但我无法解密由 C# 代码加密的字符串!我不是加密专家,谁能告诉我应该使用哪种加密算法和哪种模式?

【问题讨论】:

  • 你能改一下C#代码吗?这是相当可疑的。例如,由于您没有使用 MAC,您的服务器几乎肯定会受到 padding oracle 攻击。不使用 IV 也很糟糕。
  • 说实话,C# 代码不是我的,我没有权限。它交给了我,我应该把它转换成 PHP 就可以了。

标签: c# php encryption porting tripledes


【解决方案1】:

您的主要问题在于密钥的处理。一个问题是微不足道的:在 PHP 中,md5 函数默认返回哈希的十六进制转储,而您需要原始二进制值来匹配 C# 的MD5CryptoServiceProvider.ComputeHash()。为此,只需在 PHP 调用 (md5(key, true)) 中添加一个 true 参数。

另一个问题稍微复杂一点:MD5 返回 128 位散列,而 3DES 使用 192 位密钥(实际上,168 位密钥,其余为奇偶校验位,但我们在这里忽略它) .当您为 PHP 实现提供一个太短的键时,它是pads the key using zero bytes;但是 3DES 的 .NET/CSP 实现似乎从头开始重复密钥字节(将密钥包装起来)。因此,要匹配 PHP 中的 .NET 行为,您需要手动包装密钥 ($key . $key)。

最后一个问题是padding:PHP 使用零填充数据,而 .NET 默认使用 PKCS#7 填充。如果需要正确处理padding,需要在加密的时候加上,解密的时候去掉。

define('CIPHER', 'tripledes');
define('MODE', 'cbc');

function Encrypt($data)
{ 
    $iv = chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8);
    $key = 'abcdef';

    // determine key bytes from the key text, using MD5 and wrapping around
    $key = md5($key, true);
    $key = $key . $key;
    $tripleKey = substr($key, 0, mcrypt_get_key_size(CIPHER, MODE));

    // add PKCS#7 padding
    $blocksize = mcrypt_get_block_size(CIPHER, MODE);
    $paddingSize = $blocksize - (strlen($data) % $blocksize);
    $data .= str_repeat(chr($paddingSize), $paddingSize);

    $encodedText = mcrypt_encrypt(CIPHER, $tripleKey, $data, MODE, $iv);
    return base64_encode($encodedText);
}

function Decrypt($data)
{
    $iv = chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8);

    // determine key bytes from the key text, using MD5 and wrapping around
    $key = 'abcdef';
    $key = md5($key, true);
    $key = $key . $key;
    $tripleKey = substr($key, 0, mcrypt_get_key_size(CIPHER, MODE));

    $decodedText = mcrypt_decrypt(CIPHER, $tripleKey, base64_decode($data), MODE, $iv);

    // check and remove PKCS#7 padding
    if (!$decodedText) return $decodedText;
    $lastByte = ord($decodedText[strlen($decodedText) - 1]);
    if ($lastByte == 0 || $lastByte > mcrypt_get_block_size(CIPHER, MODE)) return FALSE;
    $paddingText = substr($decodedText, -$lastByte, $lastByte);
    $decodedText = substr($decodedText, 0, -$lastByte);
    if ($paddingText != str_repeat(chr($lastByte), $lastByte)) return FALSE;

    return $decodedText;
}

但是,正如上面的评论员已经说过的那样,由于许多原因,这并不是一个真正好的加密实现。 (而且我对调整的实施也没有真正强化。)

【讨论】:

  • 谢谢!!这对于将一些 .NET 加密方法移植到 ruby​​/openssl 非常有帮助。希望我能投票两次:D
猜你喜欢
  • 2018-03-30
  • 2021-01-15
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 2013-10-15
  • 2012-06-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多