【问题标题】:Rijndael Encryption/Decryption C# vs PHPRijndael 加密/解密 C# vs PHP
【发布时间】:2013-12-13 12:54:25
【问题描述】:

我正在尝试在客户端(C#)加密数据,然后通过 POST 将其传输到服务器并在服务器端(PHP)对其进行解码。

出于此测试目的,我还将附加到 POST 的所有值都在客户端使用以匹配服务器 值为:

  • 纯文本
  • 通过短语
  • 由客户端加密文本生成

我在服务器端重复使用这些参数,这意味着我使用相同的纯文本、相同的密码短语和相同的 IV 但是结果不匹配

客户端的加密文本与服务器端的加密文本不匹配,它们都是从相同的输入参数生成的

这是控制台输出,您可以清楚地看到发生了什么: https://dl.dropboxusercontent.com/u/15715229/ConsoleOutput.JPG

如您所见,服务器使用相同的“in”参数生成不同的哈希...

我做错了什么?

这是我的代码:

C# 代码:

static void Main(string[] args)
    {
        string url = "http://localhost/temp.php";
        WebClient web = new WebClient();

        string plainText = "This is sentence I want to encrypt";
        string passPhrase = "MyPassPhrase";
        string IV = DateTime.Now.ToLongTimeString() + "InVector";

        Console.WriteLine("");
        Console.WriteLine("----- Start Client -----");
        Console.WriteLine("Plain text = " + plainText);
        Console.WriteLine("PassPhrase = " + passPhrase);
        Console.WriteLine("IV = " + IV);

        string encryptedText = Encrypt(plainText, passPhrase, IV);
        Console.WriteLine("Encrypted Text = " + encryptedText);

        string decryptedText = Decrypt(encryptedText, passPhrase, IV);
        Console.WriteLine("Decrypted Text = " + decryptedText);
        Console.WriteLine("----- End Client -----");
        Console.WriteLine("");

        NameValueCollection postData = new NameValueCollection();
        postData.Add("plainText", plainText);
        postData.Add("encryptedText", encryptedText);
        postData.Add("passPhrase", passPhrase);
        postData.Add("IV", IV);

        string webData = Encoding.UTF8.GetString(web.UploadValues(url, "POST", postData));
        Console.WriteLine("----- Start Server Respond -----");
        Console.WriteLine(webData);
        Console.WriteLine("----- End Server Respond -----");
    }

    public static string Encrypt(string plainText, string passPhrase, string IV)
    {
        byte[] initVectorBytes = Encoding.UTF8.GetBytes(IV);
        byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
        byte[] keyBytes = Encoding.UTF8.GetBytes(passPhrase);

        RijndaelManaged symmetricKey = new RijndaelManaged();
        symmetricKey.Mode = CipherMode.CBC;

        ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);

        MemoryStream memoryStream = new MemoryStream();
        CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
        cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
        cryptoStream.FlushFinalBlock();

        byte[] cipherTextBytes = memoryStream.ToArray();
            memoryStream.Close();
            cryptoStream.Close();

        return Convert.ToBase64String(cipherTextBytes);
    }

    public static string Decrypt(string cipherText, string passPhrase, string IV)
    {
        byte[] initVectorBytes = Encoding.UTF8.GetBytes(IV);
        byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
        byte[] keyBytes = Encoding.UTF8.GetBytes(passPhrase);

        RijndaelManaged symmetricKey = new RijndaelManaged();
        symmetricKey.Mode = CipherMode.CBC;

        ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);

        MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
        CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);

        byte[] plainTextBytes = new byte[cipherTextBytes.Length];
        int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
            memoryStream.Close();
            cryptoStream.Close();

        return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
    }

我的 PHP 代码:

<?php

if(isset($_POST['plainText']))
{
    $plainText = $_POST['plainText'];
    $clientEncryptedText = $_POST['encryptedText'];
    $passPhrase = $_POST['passPhrase'];
    $iv = $_POST['IV'];

    echo "Plain text = ".$plainText."\n";
    echo "PassPhrase = ".$passPhrase."\n";
    echo "IV = ".$iv."\n";

    $encryptedText = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $passPhrase, $plainText, MCRYPT_MODE_CBC, $iv ));
    echo "Server Encrypted Text = ".$encryptedText."\n";
    echo "Client Encrypted Text = ".$clientEncryptedText."\n";

    $decryptedText = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $passPhrase, base64_decode($encryptedText), MCRYPT_MODE_CBC, $iv );
    echo "Server Decrypted Text = ".$decryptedText."\n";

    $decryptedText = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $passPhrase, base64_decode($clientEncryptedText), MCRYPT_MODE_CBC, $iv );
    echo "Decrypted text from Client = ".$decryptedText."\n";

}
else
{
    echo "POST is not set";
}

你能告诉我我在哪里做错了吗?在客户端 (C#) 还是在服务器 (PHP)?

问候 瓦迪姆斯·布里克辛斯

【问题讨论】:

  • 也许 C# 和 PHP 实现默认在不同的模式下工作(CBC、ECB)?
  • 如果您检查代码,您会看到我专门为客户端和服务器设置了相同的“CBC”模式
  • 我是否正确理解您正在传输解码加密消息所需的所有内容以及加密消息?是什么阻止了攻击者使用您免费提供的所有必要信息解密邮件?
  • 在问题描述中我解释说我只是为了测试目的而将所有内容一起传输,以消除在服务器端可能错误生成/输入错误的事实。一旦加密开始工作,我显然会从 POST 中删除敏感数据并随机生成 IV。但现在它都是静态的并且以简单的方式传输。

标签: c# php encryption rijndael


【解决方案1】:

您的密码不是适当长度的密钥。 IV也是如此。因此,会发生某种填充、截断或散列。 PHP 和 C# 的做法可能不同。此外,您没有指定在 C# 中是否使用 AES-128 或 AES-256 - 因此,您可能在 C# 中使用 AES-256,同时使用 AES-128 进行解密。理论上,C# 也可以使用不同的块大小(它可能不会)。填充也可能不同,这可能会导致以后出现问题。

确保您的 IV 与使用的块大小匹配(应为 128 位 = 16 字节),并且密码/密钥与您选择的任何密钥大小匹配。

如果您将在实践中使用真正的密码短语,则需要使用 PBKDF2 之类的东西从密码中派生密钥。

您可能还想添加完整性检查(例如,将 HMAC 与 单独的 键一起使用)。

另外,如果没有必要,不要自己实施加密。检查 SSL/TLS 是否可以为您解决问题,然后尽可能使用它。如果您愿意,您可以使用硬编码的自签名证书并且它符合您的要求,但使用现有的加密协议通常比构建自己的更好。

【讨论】:

  • 非常感谢,看起来你是对的,我也感觉IV和PassPhrase有问题你能告诉我128的情况下PassPhrase的大小应该是多少? IV = 16?我仍在测试中,如果这不起作用将您的答案标记为正确答案并上传新的固定代码
  • @Briksins:如果您将密钥长度配置为 128 位并将块大小保留为 128,那么密钥和 IV 都是 128 位 = 16 字节长。
【解决方案2】:

终于整理好了。一整天都在与它斗争,现在很想与您分享代码。

代码 100% 正常工作 - 经过测试和验证!

C# CryptoMaster.cs 文件的内容(客户端):

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace EncryptionClient
{
    class CryptoMaster
    {
        private string encryptedText;

        public void StartEncryption()
        {
            Console.WriteLine("");
            Console.WriteLine("----- Client Start -----");
            string plainText = "Hello, this is a message we need to encrypt";
            Console.WriteLine("Plain Text = " + plainText);
            string passPhrase ="Pass Phrase Can be any length";
            string saltValue = DateTime.Now.ToLongTimeString(); //slat should be 8 bite len, in my case im using Time HH:MM:SS as it is dynamic value
            string hashAlgorithm = "SHA1";
            int passwordIterations = 1;
            string initVector = "InitVector Should be 32 bite len";
            int keySize = 256;

            encryptedText = Encrypt(plainText, passPhrase, saltValue, hashAlgorithm, passwordIterations, initVector, keySize);
            Console.WriteLine("Encrypted Text = " + encryptedText);

            string decryptedText = Decrypt(encryptedText, passPhrase, saltValue, hashAlgorithm, passwordIterations, initVector, keySize);
            Console.WriteLine("Decripted Text = " + decryptedText);
            Console.WriteLine("----- Client End -----");

            SendDataToWebServer(plainText, passPhrase, saltValue, hashAlgorithm, passwordIterations, initVector, keySize);
        }

        private void SendDataToWebServer(string plainText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
        {

            NameValueCollection POST = new NameValueCollection();
            //NOTE: I'm Including all this data to POST only for TESTING PURPOSE 
            //and to avoid manual entering of the same data at server side.
            //In real live example you have to keep sensative data hidden
            POST.Add("plainText", plainText);
            POST.Add("passPhrase", passPhrase);
            POST.Add("saltValue", saltValue);
            POST.Add("hashAlgorithm", hashAlgorithm);
            POST.Add("passwordIterations", passwordIterations+"");
            POST.Add("initVector", initVector);
            POST.Add("keySize", keySize+"");
            POST.Add("encryptedText", encryptedText);


            WebClient web = new WebClient();
            string URL = "http://localhost/Encryptor.php";
            Console.WriteLine("");
            string serverRespond = Encoding.UTF8.GetString(web.UploadValues(URL, "POST", POST));
            Console.WriteLine("----- Server Start -----");
            Console.WriteLine(serverRespond);
            Console.WriteLine("----- Server End -----");

        }

        public string Encrypt(string plainText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
        {

            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

            Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, saltValueBytes, passwordIterations);

            byte[] keyBytes = password.GetBytes(keySize / 8);

            RijndaelManaged symmetricKey = new RijndaelManaged();
            symmetricKey.BlockSize = 256;
            symmetricKey.KeySize = 256;
            symmetricKey.Padding = PaddingMode.Zeros;
            symmetricKey.Mode = CipherMode.CBC;

            ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);

            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

            cryptoStream.FlushFinalBlock();
            byte[] cipherTextBytes = memoryStream.ToArray();

            memoryStream.Close();
            cryptoStream.Close();

            string cipherText = Convert.ToBase64String(cipherTextBytes);

            return cipherText;
        }

        public static string Decrypt(string cipherText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
        {

            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
            byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

            Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, saltValueBytes, passwordIterations);

            byte[] keyBytes = password.GetBytes(keySize / 8);

            RijndaelManaged symmetricKey = new RijndaelManaged();
            symmetricKey.BlockSize = 256;
            symmetricKey.KeySize = 256;
            symmetricKey.Padding = PaddingMode.Zeros;
            symmetricKey.Mode = CipherMode.CBC;

            ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);

            MemoryStream memoryStream = new MemoryStream(cipherTextBytes);

            CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);

            byte[] plainTextBytes = new byte[cipherTextBytes.Length];

            int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);

            memoryStream.Close();
            cryptoStream.Close();

            string plainText = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);

            return plainText;
        }
    }
}

PHP Encryptor.PHP 文件内容(服务器端):

<?php
error_reporting(0);


if (isset($_POST['plainText'])) {

    $plainText = $_POST['plainText'];
    $passPhrase = $_POST['passPhrase'];
    $saltValue = $_POST['saltValue'];
    $hashAlgorithm = $_POST['hashAlgorithm'];
    $passwordIterations = $_POST['passwordIterations'];
    $initVector = $_POST['initVector'];
    $keySize = $_POST['keySize'];
    $clientEncryptedText = $_POST['encryptedText'];

    $key = getKey($passPhrase,$saltValue, $passwordIterations, $keySize, $hashAlgorithm);

    echo "Plain Text = ".$plainText."\n";
    echo "Client Encrypted Text = ".$clientEncryptedText."\n";

    $encryptedText = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plainText, MCRYPT_MODE_CBC, $initVector));
    echo "Server Encrypted Text = ".$encryptedText."\n";

    $decryptedText = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($encryptedText), MCRYPT_MODE_CBC, $initVector), "\0");
    echo "Server Decrypted Text = ".$decryptedText."\n";

    $decryptedText = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($clientEncryptedText), MCRYPT_MODE_CBC, $initVector), "\0");
    echo "Client Decrypted Text = ".$decryptedText;

}

function getKey( $passPhrase, $saltValue, $passwordIterations, $keySize, $hashAlgorithm ) {

    $hl = strlen(hash($hashAlgorithm, null, true));
    $kb = ceil($keySize / $hl);
    $dk = '';

    for ( $block = 1; $block <= $kb; $block ++ ) {

        $ib = $b = hash_hmac($hashAlgorithm, $saltValue . pack('N', $block), $passPhrase, true);

        for ( $i = 1; $i < $passwordIterations; $i ++ )

            $ib ^= ($b = hash_hmac($hashAlgorithm, $b, $passPhrase, true));

        $dk .= $ib;
    }

    return substr($dk, 0, $keySize);
}

?>

控制台Output can be viewed by this link

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-26
    • 2012-05-06
    • 1970-01-01
    • 2012-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    相关资源
    最近更新 更多