Rijndael加密/解密C#与PHP

时间:2013-12-13 12:54:25

标签: c# php encryption rijndael

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

对于此测试目的,我还附加到POST,客户端使用所有值来匹配服务器 值是:

  • 纯文本
  • Pass Phrase
  • IV
  • 由客户端加密文本生成

这些参数我在服务器端重新使用,这意味着我使用相同的纯文本,相同的密码短语和相同的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)?

此致 Vadims Briksins

2 个答案:

答案 0 :(得分:1)

您的密码短语不是适当长度的密钥。 IV同样如此。因此,会发生某种填充,截断或散列。 PHP和C#很可能采用不同的方式。此外,您没有指定是否要在C#中使用AES-128或AES-256 - 因此,您可能在C#中使用AES-256,而使用AES-128进行解密。理论上,C#也可以使用不同的块大小(可能不会)。填充也可能有所不同,这可能会在以后引起问题。

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

如果你在实践中使用真正的密码短语,你需要使用类似PBKDF2的东西来从中获取密钥。

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

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

答案 1 :(得分:0)

终于把它排序了。整天都在与它斗争,现在很乐意与你分享代码。

代码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