2010-08-17 20 views
12

Tengo un algoritmo de cifrado/descifrado escrito en C# - Necesito poder producir el mismo cifrado en PHP para poder enviar el texto cifrado a través de HTTP para descifrarlo en el lado C# . Aquí está el código C# para el cifrado.Reescribir Rijndael 256 C# Código de cifrado 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; 
} 

Tengo un código PHP similar que puede ayudar. No funciona exactamente como se necesita, pero creo que puede ser un buen lugar para comenzar.

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

?>

Respuesta

17

Es necesario derivar la clave de la frase de la misma manera que el código C# en el PasswordDeriveBytes. Esto está documentado que hacer derivación de claves PBKDF1, como por RFC2898:

Esta clase utiliza una extensión del algoritmo PBKDF1 definido en el PKCS # 5 v2.0 estándar para derivar bytes adecuados para su uso como material de claves de una contraseña . El estándar está documentado en IETF RRC 2898.

hay bibliotecas de PHP que implementan PBKDF1 por ahí, pero es muy fácil de escribir uno desde cero basado ont él RFC:

PBKDF1 (P, S, c, dkLen)

Opciones: Hash
subyacente función hash

de entrada: P
contraseña, una cadena de octetos S sal, un ocho octeto cadena c iteración recuento, un dkLen entero positivo pretende longitud en octetos de clave derivada, un número entero positivo, como máximo 16 para MD2 o MD5 y 20 para SHA -1

de salida: DK deriva clave, una cadena dkLen-octeto

Pasos:

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. 

Actualizado

Cuando encuentre ti mismo en esta situación, por lo general, la búsqueda de un ejemplo implementaiton que muestra los valores en cada paso. por ejemplo el 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 

Así que ahora le permite escribir una función PHP que hace esto:

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

ya se puede ver errs de sus formas: no ha especificado el muy importante raw=true parámetro a sha1.Vamos a ver lo que es nuestra salida de la función:

$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/>'; 

esta salida es exactamente el resultado esperado:

Key:dc19847e05c64d2f 
IV:af10ebfb4a3d2a20 
Expected: DC19847E05C64D2FAF10EBFB4A3D2A20 

A continuación, se puede validar que la función C# hace lo mismo:

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

este produce la misma salida:

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

QED

bono explicación

Por favor, no hacer cripto si usted no sabe exactamente lo que está haciendo. Incluso después de obtener la implementación de PHP correcta, su código C publicado tiene algunos problemas serios. Está mezclando matrices de bytes con agitación que representa volcados hexadecimales, utiliza un IV codificado duro en lugar de derivarlo de la frase de contraseña y sal, simplemente está completamente equivocado. Utilice un esquema de cifrado comercial, como SSL o S-MIME, y no reinvente el suyo. Lo obtendrá incorrecto.

+0

Gracias por la respuesta, creo que entiendo lo que está tratando de decir aquí, pero no lo suficiente como para realmente ponerlo en uso. ¿Hay algún ejemplo completamente codificado? –

+0

Esta es la función de PHP que he creado para PBKDF1 $ HashPassPhrase = "passpharse"; $ HashSalt = "saltvalue"; $ HashIterations = 2; $ devkeylength = 32; $ devkey = PBKDF1 ($ HashPassPhrase, $ Hashsalt, $ HashIterations, $ devkeylength); \t función PBKDF1 ($ pase, $ sal, $ recuento, $ dklen) \t { \t \t $ t = sha1 ($ pase.$ sal); \t \t for ($ i = 1; $ i <$ contar; $ i ++) \t \t { \t \t \t $ t = sha1 ($ t); \t \t} \t \t $ t = substr ($ t, 0, $ dklen-1); \t \t return $ t; \t} Pero no creo que esté obteniendo los resultados correctos. –

+0

Su función PBKDF1 está usando $ dklen-1 ... ¿por qué? –

4

Parece que su problema principal es que está usando PHP hash() en lugar del paso PasswordDeriveBytes() en el lado C#. Esos dos métodos no son equivalentes. Este último implementa el algoritmo de derivación de contraseña PBKDF1, mientras que hash() es solo un hash. Parece PEAR might have a PBKDF1 implementation, pero de lo contrario es posible que tenga que escribirlo usted mismo.

También debe asegurarse de que la codificación de texto sea coherente en ambos lados, si no lo ha hecho aún.

Por último, debería considerar no hacer lo que está haciendo porque cryptography is harder than it looks. Como usa HTTP, puede usar el protocolo SSL en lugar de escribir el suyo. Esto generará una seguridad mucho mejor y menos problemas en los detalles de bajo nivel, como mantener IVs incrementales sincronizados y todo eso.

+0

Gracias por esta respuesta. Sí, me di cuenta de que el problema principal es con PasswordDeriveBytes(). ¿Está diciendo que pasar una contraseña de texto claro a través de HTTPS con SSL es más seguro que codificarla en un lado y decodificarla en el otro? –

+0

@Derek Armstrong: ¡sí! SSL está diseñado para proteger cosas como contraseñas de texto plano, tal como lo es tu esquema. La principal diferencia es que SSL ha sido revisado y revisado por los mejores criptógrafos del planeta durante los últimos 15 años. Creo que también será mucho más fácil de usar, especialmente porque ya estás hablando por HTTP. – ladenedge

+1

He agregado una nota a la respuesta de Remus también, PasswordDeriveBytes es un [esquema de propiedad] incoherente e indocumentado (http://bouncy-castle.1462172.n4.nabble.com/NET-PasswordDeriveBytes-td1462616.html) para todos bytes solicitados sobre la salida máxima de PBKDF1 (20 bytes). –

0

Compruebe las rutinas OpenSSL en PHP, deberían poder manejar lo que necesita hacer.