viernes, 9 de junio de 2017

Encriptación Rijndael con PHP y .NET

Según la Wikipedia, "AES es uno de los algoritmos más populares usados en criptografía simétrica".

Trabajando en un proyecto PHP, tuve que acceder a un web service desarrollado en .NET que utiliza AES para encriptar la petición y la respuesta.

Como sólo tenía un texto original y el encriptado, comencé por hacer un proyecto en .NET para verificar el funcionamiento.
Hasta ahí todo bien.

Para la implementación en PHP, como no tenía ni idea de cómo hacerlo, lo primero que hice fue pasarme un par de días rebuscando en Internet, probando distintas soluciones y, al final, utilizando un trozo de aquí y otro de allá, dí con la solución.
Conseguí que la encriptación / desencriptación entre PHP y .NET cuadraran.

El principal problema fue el modo de relleno (el webservice utiliza PKCS #7) que se utiliza para rellenar totalmente los bloques.

De hecho, la función que utilizo para el relleno de los bloques, es una copia de una función que encontré en Stack Overflow. Los únicos cambios que le hice son para utilizarla adaptarla a una clase y poco más.

Todo iba bien hasta que actualicé a PHP 7.1. Las funciones mcrypt_xxx han desaparecido. Se declararon obsoletas en PHP 5.5 y en PHP 7.1 se han eliminado.

Si hubiera leído tranquilamente la documentación, en lugar de ir directamente a la descripción de parámetros y ejemplos... me hubiera ahorrado problemas. Me tocó volver a rehacerlo todo utilizando OpenSSL.

No fue tan sencillo como cambiar unas funciones por otras.

Como Rijndael no es exactamente igual que AES, casi todo lo que encontré eran consultas de gente pidiendo ayuda porque no conseguían cuadrar el cifrado de PHP con .NET
Hay cientos de páginas con el mismo problema con este algoritmo. Muchas implementaciones funcionan correctamente, pero casi todo lo que encontré está basado en Mcrypt.

Al final, con la documentación de PHP en la mano, en lugar de intentar que todo funcionara de una vez, fui partiendo el problema en trocitos y conseguí echarlo a andar.
Una vez que todo funcionaba, lo metí todo en una clase, revisé todo el código, y se quedó en menos de la mitad de líneas.

He publicado en GitHub un par de clases, en C# y PHP, por si pudieran servirte para algo.

Las dos clases solo tienen un par de métodos para encriptar / desencriptar:

La clase de C# sólo tiene dos métodos estáticos:
string original = "This is a text to encrypt!";
string password = "ThisIsMyPassword";

string encriptado = SimpleRijndael.Encrypt(original, password);
string desencriptado = SimpleRijndael.Decrypt(encriptado, password);

La clase de PHP también utiliza solamente un par de métodos:
include 'RijndaelOpenSSL.php';

$original = 'This is a text to encrypt!';
$pass ='ThisIsMyPassword';

$rijndael = new RijndaelOpenSSL();
$encriptado = $rijndael->encrypt($original, $pass);
$desencriptado = $rijndael->decrypt($encriptado, $pass);

No soy experto en seguridad y algoritmos de cifrado; no sabría decirte si tiene algún fallo de seguridad.
A mí me ha servido para solucionar el enlace con el web service.

No hay comentarios:

Publicar un comentario

Coalesce C#

En programación es bastante común que se compruebe si una variable "tiene valor" y si es así, se utiliza, y en caso contrario se u...