martes, 8 de abril de 2008

Criptografía en PHP

La criptografía nos permite cifrar información para que pueda ser accedida sólo por las personas adecuadamente autorizadas.

En informática la criptografía se puede dividir en dos grupos:
  • Criptografía simétrica: la encriptación y desencriptación se realiza con la misma llave.
  • Criptografía asimétrica: la encriptación se realiza con una llave y la desencriptación con otra diferente.
Muchos modelos de transmisión de información segura utilizan una combinación de ambos métodos para combinar sus ventajas.

El término "llave", "clave" o en ingles "key" se utiliza para identificar la secuencia de bits utilizada como "clave secreta" para el proceso de cifrar/descifrar la información.

Una regla estándar en la criptografía es que la seguridad debe enfocarse en proteger las llaves de encriptación y no en la ocultación del algoritmo criptográfico (o cipher). De hecho es muy usual que los algoritmos sean públicos.


Criptografía simétrica
La criptografía simétrica se basa en que la misma llave de encriptación se utiliza para desencriptar.

Es relativamente rápido, fácil de implementar e implementable en hardware de bajo costo. Presenta el problema que el emisor y receptor deben conocer previamente la llave de encriptación. Esto nos lleva al problema de cómo transmitirle al receptor la llave de encriptación y este es precisamente el principal problema de este modelo.

Antiguamente el algoritmo más popular era DES (Data Encryption Standard), sin embargo en la actualidad es considerado un algoritmo inseguro ya que la llave de encriptación tiene una longitud de sólo 56 bits, una vez que se declaró este algoritmo como inseguro, se comenzó a utilizar Triple DES que tiene una llave de 158 bits.

En la actualidad el algortimo de cifrado simétrico más popular es AES (Advanced Encryption Standard) también conocido como Rijndael (que es una combinación de lo nombre de sus autores) y la llave puede tener una longitud de 128, 196 y 256 bits.

A continuación incluyo un ejemplo de crifrado utilizando Rijndael-256, es decir utilizamos una llave de 256 bits que es equivalente a los 32 caracteres del ejemplo:
<?php

// Datos de entrada
$texto = 'frase secreta';
$key = '12345678901234567890123456789012';

// Proceso de cifrado
$iv = 'abcdefghijklmnopqrstuvwxyz012345';
$td = mcrypt_module_open('rijndael-256', '', 'ecb', '');
mcrypt_generic_init($td, $key, $iv);
$texto_cifrado = mcrypt_generic($td, $texto);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);

// Opcionalmente codificamos en base64
$texto_cifrado = base64_encode($texto_cifrado);

echo "$texto_cifrado\n";

?>
Este ejemplo requiere que PHP tenga habilitado el módulo mcrypt.

A continuación muestro un ejemplo del proceso inverso donde se desencripta la información previamente encriptada:
<?php

// Opcionalmente descodificamos en base64
$texto_cifrado = base64_decode($texto_cifrado);

// Proceso de descifrado
$td = mcrypt_module_open('rijndael-256', '', 'ecb', '');
mcrypt_generic_init($td, $key, $iv);
$texto = mdecrypt_generic($td, $texto_cifrado);
$texto = trim($texto, "\0");

echo "$texto\n";

?>

Criptografía asimétrica
La criptografía asimétrica tiene la característica que utiliza una llave para cifrar y otra para descifrar. Usualmente a una de estas llaves se le denomina clave privada y a la otra clave pública. La clave privada es conocida solo por el propietario de la misma, la clave pública puede ser conocida por todos. Gracias a este modelo se soluciona el principal problema de la criptografía simétrica.

Sin embargo tiene el inconveniente que los algoritmos criptográficos son más complejos, requieren mayor tiempo de procesamiento y el mensaje cifrado requiere de mayor longitud que el mensaje original. Además para proveer el mismo nivel de dureza que la criptografía simétrica (con respecto a ataques de fuerza bruta) requiere de llaves de mayor longitud, como por ejemplo una llave simétrica de 128 bits es aproximadamente equivalente a una llave asimétrica de 1024 bits.

Gracias a la criptografía asimétrica es posible garantizar -entre otros- lo siguiente:
  • Confidencialidad: Codificar información que solo la pueda descifrar el receptor.
  • Autenticación: Validar que la persona sea quien dice ser.
  • Integridad: Asegurar que la información no haya sido alterada (ej: firma electrónica).
  • No repudio: Asegurar quién es el autor de cierta información.
Más adelante presentaré un ejemplo de cifrado asimétrico, pero antes de eso publicaré un artículo de openssl para enseñar a generar certificados digitales y llaves.


Firma electrónica
La firma electrónica (que es derivada de la criptografía asimétrica) se ha convertido en una herramienta escencial en el desarrollo de aplicaciones y es un estándar de la industria y de gobierno electrónico (e-gov) para el tratamiento de información.

La firma electrónica está construida sobre la infraestructura de llave pública (PKI) y permite asegurar la autoría e integridad de los documentos.

Usualmente la implementación de firma electrónica se realiza en base a la especificación de XML Signature [w3c] también conocida como xmldsig.

Actualmente PHP no implementa XML Signature de manera nativa, sin embargo desde PHP 5.2.1 es posible implementarla (planeo a futuro dar algunos ejemplos de su implementación). Además Rob Richards que es miembro del equipo de desarollo de PHP (autor -entre otros- del módulo DOM) está trabajando en un módulo para PHP que ojalá algún día vea la luz.

8 comentarios:

Manu dijo...

Muy buenas.
Estoy tremendamente interesado en implentar la firma electrónica mediante PHP.

Hay un applet muy interesante http://cryptoapplet.nisu.org/ desarrollado por una universidad española. Dicho applet te genera entre otros un XML (XADES) que es enviado al servidor, pero una vez en el servidor no sé que tengo que hacer para verificar el certificado firmante y el documento firmado.
¿Tengo que utilizar OpenSSL?

Gracias.

José Rodríguez dijo...

En PHP puedes utilizar OpenSSL o GNUPG.

Con OpenSSL puedes utilizar la función openssl_verify().

Si vas a validar un documento firmado con XADES o XMLDSIG, primero debes extraer la porción del XML firmado y luego canonicalizarlo antes de validar la firma.

Manu dijo...

¿canonizarlo? eso se puede hacer en PHP.
Vas a publicar ejemplos de verificacion de firma?

José Rodríguez dijo...

Si, es posible canonicalizar en PHP, aunque creo que aún es una funcionalidad aún no documentada, pero según recuerdo está disponible desde PHP 5.2.1.

A futuro planeo publicar información y ejemplos sobre firma electrónica.

Anónimo dijo...

Quisiera que por favo me ayuden en averiguar si puedo implementar un sistema de envio de documentos aplicando firmas digitales con php se los agradecere bastante.....

nosolopalabras dijo...
Este comentario ha sido eliminado por el autor.
nosolopalabras dijo...

yo también estoy muy interesado en el tema de cifrado de xml mediante php. Estoy desarrollando una aplicacion web para un cliente que necesita firmar los documentos xml antes de transmitirlos a las entidades gubernamentales :(

¿alguna idea, tutorial o guía por donde pueda empezar?

Rodrigo Jacome dijo...

Como puedo obtener el contenido de los archivos .cer y .key con PHP???