C#和PHP上的三重DES加密会产生不同的结果

时间:2012-11-15 17:21:33

标签: c# php encryption 3des

我正在编写一个简单的登录加密系统,但我的问题很小。 C#加密功能:

public static string EncryptString(string Message, string Passphrase)
{
    byte[] Results;
    System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();

    // Step 1. We hash the passphrase using MD5
    // We use the MD5 hash generator as the result is a 128 bit byte array
    // which is a valid length for the TripleDES encoder we use below

    MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
    byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));

    // Step 2. Create a new TripleDESCryptoServiceProvider object
    TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();

    // Step 3. Setup the encoder
    TDESAlgorithm.Key = TDESKey;
    TDESAlgorithm.Mode = CipherMode.ECB;
    TDESAlgorithm.Padding = PaddingMode.PKCS7;

    // Step 4. Convert the input string to a byte[]
    byte[] DataToEncrypt = UTF8.GetBytes(Message);

    // Step 5. Attempt to encrypt the string
    try
    {
        ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
        Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
    }
    finally
    {
        // Clear the TripleDes and Hashprovider services of any sensitive information
        TDESAlgorithm.Clear();
        HashProvider.Clear();
    }

    // Step 6. Return the encrypted string as a base64 encoded string
    return Convert.ToBase64String(Results);
}

EncryptString("test", "123456")返回"Yjaqhc7RFds="

php中的相同代码:

  <?php
    $key = "123456";
    function pkcs7_pad($text, $blocksize)
    {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }

    $input = pkcs7_pad("test", 16);
    $key = md5(utf8_encode($key), true);
    $td = mcrypt_module_open('tripledes', '', 'ecb', '');
    $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
    mcrypt_generic_init($td, $key, $iv);
    $encrypted_data = mcrypt_generic($td, $input);
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);

    echo base64_encode($encrypted_data);
?>

返回"dybhiZYdKG8pNCgCFkbV6g=="?我做错了什么?

1 个答案:

答案 0 :(得分:7)

您遇到此问题,因为Triple DES的密钥大小为168位(21字节),但MD5生成的哈希值仅为16字节(128位)。

这意味着密钥必须扩展到168位,因此Triple DES可以工作。事实证明,这种从128位到168位的推导在C#中的工作方式与在PHP中的工作方式不同,因此有效使用的密钥不同,导致加密数据不同。

现在您有两个选择:


选项1:选择一个直接支持128位密钥的密码

如果使用支持128位密钥的密码,则可以避免与密钥大小差异相关的所有问题。这需要对代码进行最少的更改。例如,您可以使用Rijndael(AES)。

C#:将TripleDESCryptoServiceProvider更改为RijndaelManaged 其他所有东西都可以保持不变。 (Demo)

PHP :使用MCRYPT_RIJNDAEL_128代替三重(Demo)

function encrypt_pkcs7($str, $key)
{
    $key = md5(utf8_encode($key), true);
    $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
    $pad = $block - (strlen($str) % $block);
    $str .= str_repeat(chr($pad), $pad);

    $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB);
    return base64_encode($ciphertext);
}

echo encrypt_pkcs7('test', '123456');

请注意,AES的有效密钥大小大于Triple DES。虽然Triple DES'密钥长168位,但它只提供112位安全性。如果我是你,我会选择这个选项。


选项2:使用192位密钥而不是128位密钥

如果使用比Triple DES实际使用的更大的密钥,C#和PHP似乎同意如何将其减少到168位。你可以使用像SHA-256这样的哈希函数来做到这一点,它生成一个256位的哈希并将其修剪为192位(24字节):

C#:使用SHA256CryptoServiceProviderArray.Copy获取192位密钥,并将其用于程序的其余部分:(Demo)

SHA256CryptoServiceProvider HashProvider = new SHA256CryptoServiceProvider();
byte[] temp = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));
byte[] key = new byte[24];
Array.Copy(temp, key, 24);

PHP :将hash()与SHA-256和substr()一起使用以获取192位密钥

function encrypt_pkcs7($str, $key)
{
    // derive 192-bit key using SHA-256
    $key = substr(hash('sha256', $key, true), 0, 24);
    $block = mcrypt_get_block_size(MCRYPT_3DES, MCRYPT_MODE_ECB);
    $pad = $block - (strlen($str) % $block);
    $str .= str_repeat(chr($pad), $pad);

    $ciphertext = mcrypt_encrypt(MCRYPT_3DES, $key, $str, MCRYPT_MODE_ECB);
    return base64_encode($ciphertext);
}

echo encrypt_pkcs7('test', '123456');

看起来我不能向TripleDESCryptoServiceProvider提供168位密钥,我不知道为什么。


其他考虑因素:

  • 考虑使用SSL;即使这是一个自签名的证书。它重新发明了轮子,当涉及密码学时,这是一项特别危险的任务。

  • 考虑使用ECB以外的操作模式(例如:CBC)。使用ECB会增加安全风险。阅读Wikipedia article on block cipher modes of operation

  • 除非您绝对需要明文密码(很少是这种情况),否则您应该对密码进行哈希处理。阅读this article on securing passwords.

  • 考虑使用正确的基于密码的密钥派生函数,如PBKDF2,而不是像MD5或SHA系列这样的通用哈希函数。这将使关键难度更大。有关更多信息,请阅读上一个要点中的文章。