3

ログイン用の単純な暗号化システムを作成していますが、小さな問題があります。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=="か?私は何を間違っていますか?

4

1 に答える 1

8

Triple DES のキー サイズは 168 ビット (21 バイト) ですが、MD5 は 16 バイト (128 ビット) の長さしかないハッシュを生成するため、この問題が発生しています。

これは、トリプル DES が機能するように、キーを 168 ビットに拡張する必要があることを意味します。この 128 ビットから 168 ビットへの導出は、C# では PHP とは異なる動作をすることが判明したため、効果的に使用されるキーが異なり、結果として異なる暗号化データが生成されます。

次の 2 つのオプションがあります。


オプション 1: 128 ビット キーを完全にサポートする暗号を選択する

128 ビット キーをサポートする暗号を使用すると、キー サイズの違いに関連するすべての問題を回避できます。これにより、コードに最小限の変更が必要になります。たとえば、Rijndael (AES) を使用できます。

C# : に変更TripleDESCryptoServiceProviderRijndaelManagedます。
他のすべてはほとんど同じままでかまいません。(デモ)

PHP : MCRYPT_RIJNDAEL_128tripledes の代わりに使用(デモ) :

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: 128 ビット キーの代わりに 192 ビット キーを使用する

Triple DES が実際に使用するよりも大きなキーを使用する場合、C# と PHP はそれを 168 ビットに減らす方法について合意しているようです。これを行うには、SHA-256 のようなハッシュ関数を使用します。これは 256 ビットのハッシュを生成し、それを 192 ビット (24 バイト) にトリムします。

C# : と を使用SHA256CryptoServiceProviderArray.Copyて 192 ビット キーを取得し、それをプログラムの残りの部分で使用します: (デモ)

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');

に 168 ビット キーを提供できないようTripleDESCryptoServiceProviderです。理由はわかりません。


その他の考慮事項:

  • SSL の使用を検討してください。自己署名証明書であっても。これは、暗号化が関係する場合に特に危険な作業である、車輪の再発明よりも優れています。

  • ECB 以外の操作モードの使用を検討してください (例: CBC)。ECB を使用すると、セキュリティ リスクが高まります。ブロック暗号の動作モードに関するウィキペディアの記事を読んでください。

  • プレーンテキストのパスワードがどうしても必要な場合を除き (まれなケースです)、パスワードをハッシュ化する必要があります。パスワードの保護に関するこの記事をお読みください。

  • MD5 や SHA ファミリーなどの汎用ハッシュ関数の代わりに、PBKDF2などの適切なパスワードベースのキー派生関数を使用することを検討してください。これにより、キーのクラックが難しくなります。詳細については、前の箇条書きの記事を参照してください。

于 2012-11-15T19:45:57.887 に答える