2010-08-17 30 views
12

J'ai un algorithme de cryptage/décryptage écrit en C# - Je dois pouvoir produire le même cryptage en PHP afin que je puisse envoyer le texte crypté sur HTTP pour être décrypté du côté C# . Voici le code C# pour le chiffrement.Réécriture du code de cryptage Rijndael 256 C# en PHP

this.m_plainText = string.Empty; 
this.m_passPhrase = "passpharse"; 
this.m_saltValue = "saltvalue"; 
this.m_hashAlgorithm = "SHA1"; 
this.m_passwordIterations = 2; 
this.m_initVector = "1a2b3c4d5e6f7g8h"; 
this.m_keySize = 256; 

public string Encrypt() 
{ 
    string plainText = this.m_plainText; 
    string passPhrase = this.m_passPhrase; 
    string saltValue = this.m_saltValue; 
    string hashAlgorithm = this.m_hashAlgorithm; 
    int passwordIterations = this.m_passwordIterations; 
    string initVector = this.m_initVector; 
    int keySize = this.m_keySize; 

    // Convert strings into byte arrays. 
    // Let us assume that strings only contain ASCII codes. 
    // If strings include Unicode characters, use Unicode, UTF7, or UTF8 
    // encoding. 
    byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector); 
    byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue); 

    // Convert our plaintext into a byte array. 
    // Let us assume that plaintext contains UTF8-encoded characters. 
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); 

    // First, we must create a password, from which the key will be derived. 
    // This password will be generated from the specified passphrase and 
    // salt value. The password will be created using the specified hash 
    // algorithm. Password creation can be done in several iterations. 
    PasswordDeriveBytes password = new PasswordDeriveBytes(
                passPhrase, 
                saltValueBytes, 
                hashAlgorithm, 
                passwordIterations); 

    // Use the password to generate pseudo-random bytes for the encryption 
    // key. Specify the size of the key in bytes (instead of bits). 
    byte[] keyBytes = password.GetBytes(keySize/8); 

    // Create uninitialized Rijndael encryption object. 
    RijndaelManaged symmetricKey = new RijndaelManaged(); 

    // It is reasonable to set encryption mode to Cipher Block Chaining 
    // (CBC). Use default options for other symmetric key parameters. 
    symmetricKey.Mode = CipherMode.CBC; 

    // Generate encryptor from the existing key bytes and initialization 
    // vector. Key size will be defined based on the number of the key 
    // bytes. 
    ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                keyBytes, 
                initVectorBytes); 

    // Define memory stream which will be used to hold encrypted data. 
    MemoryStream memoryStream = new MemoryStream(); 

    // Define cryptographic stream (always use Write mode for encryption). 
    CryptoStream cryptoStream = new CryptoStream(memoryStream, 
               encryptor, 
               CryptoStreamMode.Write); 
    // Start encrypting. 
    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); 

    // Finish encrypting. 
    cryptoStream.FlushFinalBlock(); 

    // Convert our encrypted data from a memory stream into a byte array. 
    byte[] cipherTextBytes = memoryStream.ToArray(); 

    // Close both streams. 
    memoryStream.Close(); 
    cryptoStream.Close(); 

    // Convert encrypted data into a base64-encoded string. 
    string cipherText = Convert.ToBase64String(cipherTextBytes); 

    // Return encrypted string. 
    return cipherText; 
} 

J'ai un code PHP similaire qui peut aider. Il ne fait pas exactement ce qui est nécessaire, mais je pense que c'est un bon point de départ.

<?php 

/* 
* DEFINE CONSTANTS 
*/ 
$HashPassPhrase = "passpharse"; 
$HashSalt = "saltvalue"; 
$HashAlgorithm = "SHA1"; 
$HashIterations = "2"; 
$InitVector = "1a2b3c4d5e6f7g8h";  // Must be 16 bytes 
$keySize = "256"; 

class Cipher { 
    private $securekey, $iv; 
    function __construct($textkey) { 
     $this->securekey = hash($HashAlgorithm,$textkey,TRUE); 
     $this->iv = $InitVector; 
    } 
    function encrypt($input) { 
     return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->securekey, $input, MCRYPT_MODE_CBC, $this->iv)); 
    } 
    function decrypt($input) { 
     return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->securekey, base64_decode($input), MCRYPT_MODE_CBC, $this->iv)); 
    } 
} 

$cipher = new Cipher($HashPassPhrase); 

$encryptedtext = $cipher->encrypt("Text To Encrypt"); 
echo "->encrypt = $encryptedtext<br />"; 

$decryptedtext = $cipher->decrypt($encryptedtext); 
echo "->decrypt = $decryptedtext<br />"; 

var_dump($cipher); 

?>

Répondre

17

Vous devez déduire la clé de la phrase de passe de la même manière que le code C# dans le fait PasswordDeriveBytes. Ceci est démontrables dérivation de clé PBKDF1, selon RFC2898:

Cette classe utilise une extension de l'algorithme de PBKDF1 défini dans le PKCS # 5 standard v2.0 pour obtenir des octets appropriés pour une utilisation en tant que matériau clé à partir d'un mot de passe . La norme est documentée dans IETF RRC 2898.

il y a des bibliothèques PHP qui mettent en œuvre PBKDF1 là-bas, mais il est vraiment simple d'écrire un à partir de zéro basé en Ontario, il RFC:

PBKDF1 (P, S, c, dkLen)

options: hash
fonction de hachage sous-jacente

entrée: P
mot de passe, une chaîne d'octets de sel, une chaîne nombre de c d'itération de huit octets, un nombre entier positif dkLen longueur destiné en octets de la clé dérivée, un entier positif, au plus 16 pour MD2 ou MD5 et 20 pour SHA -1

sortie: DK dérivé clé, une chaîne-octet dkLen

étapes:

1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output 
    "derived key too long" and stop. 

    2. Apply the underlying hash function Hash for c iterations to the 
    concatenation of the password P and the salt S, then extract 
    the first dkLen octets to produce a derived key DK: 

       T_1 = Hash (P || S) , 
       T_2 = Hash (T_1) , 
       ... 
       T_c = Hash (T_{c-1}) , 
       DK = Tc<0..dkLen-1> 

    3. Output the derived key DK. 

Mise à jour

Lorsque vous youself trouvez dans cette situation, vous effectuez une recherche habituellement pour un exemple implementaiton qui montre les valeurs à chaque étape. par exemple celui de http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf:

Password = "password" 
     = (0x)70617373776F7264 
Salt  = (0x)78578E5A5D63CB06 
Count = 1000 
kLen  = 16 
Key  = PBKDF1(Password, Salt, Count, kLen) 
     = (0x)DC19847E05C64D2FAF10EBFB4A3D2A20 

P || S = 70617373776F726478578E5A5D63CB06 
T_1=  D1F94C4D447039B034494400F2E7DF9DCB67C308 
T_2=  2BB479C1D369EA74BB976BBA2629744E8259C6F5 
... 
T_999= 6663F4611D61571068B5DA168974C6FF2C9775AC 
T_1000= DC19847E05C64D2FAF10EBFB4A3D2A20B4E35EFE 
Key=  DC19847E05C64D2FAF10EBFB4A3D2A20 

donc permet maintenant d'écrire une fonction PHP qui fait cela:

function PBKDF1($pass,$salt,$count,$dklen) { 
    $t = $pass.$salt; 
    //echo 'S||P: '.bin2hex($t).'<br/>'; 
    $t = sha1($t, true); 
    //echo 'T1:' . bin2hex($t) . '<br/>'; 
    for($i=2; $i <= $count; $i++) { 
     $t = sha1($t, true); 
     //echo 'T'.$i.':' . bin2hex($t) . '<br/>'; 
    } 
    $t = substr($t,0,$dklen); 
    return $t;  
} 

Maintenant, vous pouvez voir les errs de vos façons: vous ne spécifiez pas le tout raw=true importante paramètre à sha1.Voyons quelle est notre sortie de fonction:

$HashPassPhrase = pack("H*","70617373776F7264"); 
$HashSalt = pack("H*","78578E5A5D63CB06"); 
$HashIterations = 1000; 
$devkeylength = 16; 
$devkey = PBKDF1($HashPassPhrase,$HashSalt,$HashIterations,$devkeylength); 
echo 'Key:' . bin2hex(substr($devkey, 0, 8)) . '<br/>'; 
echo 'IV:' . bin2hex(substr($devkey, 8, 8)) .'<br/>'; 
echo 'Expected: DC19847E05C64D2FAF10EBFB4A3D2A20<br/>'; 

cette sortie exactement le résultat escompté:

Key:dc19847e05c64d2f 
IV:af10ebfb4a3d2a20 
Expected: DC19847E05C64D2FAF10EBFB4A3D2A20 

Ensuite, nous pouvons confirmer que la fonction C# fait la même chose:

  byte[] password = Encoding.ASCII.GetBytes("password"); 
      byte[] salt = new byte[] { 0x78, 0x57, 0x8e, 0x5a, 0x5d, 0x63, 0xcb, 0x06}; 

      PasswordDeriveBytes pdb = new PasswordDeriveBytes(
       password, salt, "SHA1", 1000); 

      byte[] key = pdb.GetBytes(8); 
      byte[] iv = pdb.GetBytes(8); 

      Console.Out.Write("Key: "); 
      foreach (byte b in key) 
      { 
       Console.Out.Write("{0:x} ", b); 
      } 
      Console.Out.WriteLine(); 

      Console.Out.Write("IV: "); 
      foreach (byte b in iv) 
      { 
       Console.Out.Write("{0:x} ", b); 
      } 
      Console.Out.WriteLine(); 

cette produit la même sortie:

Key: dc 19 84 7e 5 c6 4d 2f 
IV: af 10 eb fb 4a 3d 2a 20 

QED

d'explication bonus

S'il vous plaît ne le faites pas si Crypto vous ne savez pas exactement ce que vous faites. Même après avoir obtenu la bonne implémentation PHP, votre code C# posté a de sérieux problèmes. Vous êtes en train de mélanger des tableaux d'octets avec des hexs représentant des hexs, vous utilisez un IV codé en dur au lieu de le déduire de la phrase secrète et du sel, c'est tout simplement faux. Veuillez utiliser un schéma de chiffrement standard, tel que SSL ou S-MIME, et ne pas réinventer le vôtre. Vous l'obtiendrez erroné.

+0

Merci pour la réponse, je pense que je comprends ce que vous essayez de dire ici, mais pas assez pour le mettre en service. Est-il possible d'exemple entièrement codé? –

+0

C'est la fonction PHP que j'ai trouvé pour le PBKDF1 $ HashPassPhrase = "passpharse"; $ HashSalt = "saltvalue"; $ HashIterations = 2; $ devkeylength = 32; $ devkey = PBKDF1 ($ HashPassPhrase, $ Hashsalt, $ HashIterations, $ devkeylength); \t fonction PBKDF1 (pass $, $ sel, $ count, dkLen $) \t { \t \t $ t = SHA1 (pass $.$ sel); \t \t for ($ i = 1; $ i <$ count; $ i ++) { \t \t \t \t \t $ t = SHA1 ($ t); \t \t} \t \t $ t = substr ($ t, 0, $ dklen-1); \t \t return $ t; \t} Mais je ne pense pas que j'obtiens les bons résultats. –

+0

Votre fonction PBKDF1 utilise $ dklen-1 ... pourquoi ??? –

4

Il semble que votre problème principal est que vous utilisez PHP hash() à la place de l'étape PasswordDeriveBytes() sur le côté C#. Ces deux méthodes ne sont pas équivalentes. Ce dernier implémente l'algorithme de dérivation de mot de passe PBKDF1, tandis que hash() est juste un hachage. Il ressemble à PEAR might have a PBKDF1 implementation, mais sinon vous pourriez avoir à l'écrire vous-même.

Vous devez également vous assurer que l'encodage de votre texte est cohérent des deux côtés, si ce n'est déjà fait. Enfin, vous devriez envisager de ne pas faire ce que vous faites, car cryptography is harder than it looks. Puisque vous utilisez HTTP, vous pouvez utiliser le protocole SSL au lieu d'écrire le vôtre. Cela vous permettra de bien plus de sécurité et moins de soucis sur les détails de bas niveau, comme la synchronisation des IV incrémentiels et autres joyeusetés.

+0

Merci pour cette réponse. Oui j'ai réalisé que le problème principal est avec le PasswordDeriveBytes(). Êtes-vous en train de dire que transmettre un mot de passe en clair sur HTTPS avec SSL est plus sûr que de le coder d'un côté et de le décoder de l'autre? –

+0

@Derek Armstrong: oui! SSL est conçu pour protéger les choses comme les mots de passe en clair, tout comme votre système. La principale différence est que SSL a été révisé et révisé par les meilleurs cryptographes de la planète au cours des 15 dernières années. Je pense que ce sera aussi beaucoup plus facile à utiliser, d'autant plus que vous parlez déjà via HTTP. – ladenedge

+1

J'ai ajouté une note à la réponse de Remus aussi, PasswordDeriveBytes est un schéma propriétaire inconsistant, non documenté (http://bouncy-castle.1462172.n4.nabble.com/NET-PasswordDeriveBytes-td1462616.html) pour tous octets demandés sur la sortie maximale de PBKDF1 (20 octets). –

0

Y a-t-il une bonne raison pour laquelle vous ne pouvez pas simplement utiliser http://php.net/manual/en/function.mcrypt-module-open.php et utiliser rijndael-256 comme algorithme ????

+0

Ce module est encore plus bas que celui qu'il a déjà! – ladenedge

+0

Oui, ça ne marchera pas. J'ai besoin de SEL entre autres choses. –

0

Vérifiez les routines OpenSSL en PHP, elles devraient être capables de gérer ce que vous devez faire.