2013-12-13 47 views
0

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

对于这个测试的目的我也附加到POST所有值在客户端上使用与之相匹配的服务器 值是:

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

这些参数在SERV IM重新使用呃,这意味着我使用相同的纯文本,相同的密码短语和相同的IV 但结果不匹配

客户端的加密文本与服务器的加密文本不匹配其中,两人都来自同一个输入生成的辅助参数

这里是控制台输出,你可以清楚地看到正在发生的事情: https://dl.dropboxusercontent.com/u/15715229/ConsoleOutput.JPG

正如你看到的服务器使用相同的“in”生成不同的hash参数...

w ^我做错了什么?

这里是我的代码:

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

+0

也许C#和PHP实现在默认模式下工作在不同的模式(CBC,ECB)? – JustAPirate

+1

如果您要检查代码,您会看到我专门为客户端和服务器设置了相同的“CBC”模式 – Briksins

+0

我是否正确理解您正在传输解密加密邮件所需的所有内容以及加密邮件?什么阻止攻击者使用您提供的所有必要信息解密邮件? – knittl

回答

0

终于得到它排序。整天都在与它斗争,现在很想与你分享代码。

代码是100%的工作 - 测试和验证! PHP Encryptor.PHP文件(服务器端)的

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; 
     } 
    } 
} 

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

内容

<?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

0

你的密码是不是合适长度的密钥。 IV也一样。因此,某种填充,截断或散列会发生。 PHP和C#可能会有所不同。此外,您不指定的话AES-128或AES-256是在C#中使用 - 这样,你很可能使用AES-256在C#中,同时与AES-128解密。同时C#可以,理论上,也可以使用不同的块大小(它可能没有)。填充也可能会有所不同,这可能会导致后续问题。

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

如果要使用在实践中真正的密码短语,你需要使用类似PBKDF2从中导出密钥。

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

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

+0

非常感谢,看你对,我也有感觉问题是在IV和PassPhrase 你能告诉我在128的情况下应该是PassPhrase的大小吗?和IV = 16? 我还在测试,如果这将工作不良标记您的答案为正确的答案,并上传新的固定代码 – Briksins

+0

@Briksins:如果您配置密钥长度为128位,并保留块大小为128,那么密钥和IV每个128位= 16个字节。 –