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.

viernes, 19 de mayo de 2017

Conversión de números a texto

Hace bastante tiempo necesité un conversor para representar un importe en euros en texto; principalmente para imprimir importes de cheques, recibos, etc.

Como no encontré ninguno que se adaptara a lo que necesitaba, partiendo de uno que tenía hecho en C, lo hice en C#.

Luego decidí "traducirlo" a PHP. Partiendo de .NET es relativamente sencillo, ya que básicamente se trata de analizar un número y traducirlo.

Tienes las dos versiones disponibles en GitHub y en PHP Classes.

Se trata de una sola clase con un método estático que realiza la conversión.

Su uso es muy sencillo:

C#

string txt1 = Alp3476.A2Num2Txt.ToString("3528.25");
Console.WriteLine(txt1);
// tres mil quinientos veintiocho con veinticinco

string txt2 = Alp3476.A2Num2Txt.ToString(123456);
Console.WriteLine(txt2);
// ciento veintitres mil cuatrocientos cincuenta y seis

En la versión de C#, el método está sobrecargado, por lo que admite como parámetro un double, o un string.

PHP

include 'Num2Txt.php';

$o = new  \Alp3476\Num2Txt();
echo $o->toString(3527.25) . PHP_EOL;
// tres mil quinientos veintisiete con veinticinco

echo $o->toString(12342245281.890) . PHP_EOL;
// doce mil trescientos cuarenta y dos millones doscientos cuarenta y cinco mil ...
// ... doscientos ochenta y uno con ochenta y nueve

En PHP, el parámetro se convierte a float utilizando floatval(), por lo que admite cualquier parámetro que se pueda convertir a un número de punto flotante.

Los números se redondean a dos dígitos.
Como he comentado, su uso inicial es para imprimir importes en euros (de ahí que se utlicen dos decimales)


Por supuesto que hay muchas clases similares y seguramente más optimizadas, pero si crees que te pueden servir, ahí las tienes.

Son muy sencillas y las podrás adaptar a tus necesidades.



lunes, 15 de mayo de 2017

Cálculos con fechas

Todos los lenguajes de programación (o casi todos) disponen de un tipo de datos para representar fechas y/o  funciones para trabajar con ellas.
Sin embargo, no es raro que nos encontremos con código, (a veces antiguo, a veces no) que trata las fechas "a mano".

Este es un un ejemplo real de una función que ví en una aplicación PHP:

function sumaMes($fecha,$mesesSumar,$formatoDMA=false)
{
  $dia = $this->separaFecha($fecha,"D");
  $mes = $this->separaFecha($fecha,"M");
  $ano = $this->separaFecha($fecha,"A");
  $mesResult = $mes;

  if (!empty($mes))
  {
    for ($n = $mes; $n<($mes + $mesesSumar); $n++)
    {
      if ($n == 12)
      {
        $mesResult = 1;
        $ano++;
      }
      else
        $mesResult++;
    }
  }

  $mes = $this->cerosLeft($mesResult, 2);

  if ($formatoDMA)
    $resp = "$dia-$mes-$ano";
  else
    $resp = "$ano-$mes-$dia";

  return $resp;
}

Esta función está tal y como la encontré. En apariencia, el código parece correcto, y funciona bien hasta que hacemos operaciones como sumar un mes a un 30 de enero, lo que nos devuelve un 30 de febrero.
Este tipo de errores se detectan cuando utilizamos una base de datos y protesta cuando intentamos grabar una fecha no válida.
Si estamos creando un fichero tipo XML o similar, tenemos un problema; tarde o temprano lo descubriremos, o más bien, lo descubrirán los clientes por nosotros.

Esto de despotricar del código de otros es fácil; hay gente a la que le parece divertido.
Sin embargo, cuando heredas un código como éste y te toca mantenerlo, o tienes que hacer un traspaso de datos que ha pasado por este código, créeme, no es divertido.

La función sumaMes() lá encontré en una aplicación que utilizaba una versión antigua de MySQL que, sorprendentemente, se tragaba fechas como el 31 de abril. El traspaso de esos datos a PostgreSQL fue toda una aventura.

El tratar las fechas como texto, descomponerlas y hacer operaciones, no es lo más adecuado.
Una cosa es analizar y descomponer un texto que representa una fecha, y otra hacer operaciones con la fecha.

Vamos a ver un par de ejemplos de tratamiento de fechas:
  • Añadir / restar intervalos
  • Cálculo del último día del mes

Añadir / Restar intervalos

Vamos a ver varios ejemplos de cómo añadir 30 días, y restar un año en fechas en PHP, C#, JavaScript y PostgreSQL:

PHP

/* Sumar 30 días */
$f = new DateTime();
$f->add(new DateInterval('P30D'));
echo $f->format('d-M-Y');

/* Restar 1 año */
$f = new DateTime();
$f->sub(new DateInterval('P1Y'));
echo $f->format('d-M-Y'); 

La clase DateInterval es la que nos permite definir el intervalo con el que operamos sobre la fecha actual. Hay que tener en cuenta que se modifica el objeto original, lo que puede ser una ventaja o un inconveniente.

C#

/* Sumar 30 días */
var f= DateTime.Today;
var f2 = f.AddDays(30);
Console.WriteLine($"Original {f}  Nueva: {f2}");

/* Restar 1 año */
var f= DateTime.Today;
var f2 =f.AddYears(-1);
Console.WriteLine($"Original {f}  Nueva: {f2}");

En C#, la clase Datetime nos proporciona métodos para operar con la fecha.
A diferencia de PHP, estos métodos devuelven un nuevo objeto de tipo DateTime, manteniendo el objeto original intacto.

JavaScript

/* Sumar 30 días */
var f= new Date();
f.setDate(f.getDate() + 30);
console.log(f);

/* Restar 1 año */
var f= new Date();
f.setFullYear(f.getFullYear() - 1);
console.log(f);

En JavaScript lo que hacemos, es modificar las propiedades del objeto Date().

PostgreSQL

-- Sumar 30 días
SELECT current_date + 30
SELECT cast(current_date + INTERVAL '1 day' AS date)

-- Restar 1 año
SELECT cast(current_date - INTERVAL '1 year' AS date)

En PostgreSQL, podemos sumar o restar días directamente con un entero. También podemos utilizar INTERVAL
Al utilizar INTERVAL, nos devuelve un timestamp. Si lo que queremos es un tipo 'date', hacemos el casting.

Último día del mes

Esta es una operación muy frecuente.
Seguramente que habrás visto código en el que tenemos un array de 12 elementos que contiene los días de los meses; luego selecciona el elemento correspondiente al mes, y si es febrero, se comprueba si el año es bisiesto y devuelve 29 en lugar de 28.

Dependiendo del lenguaje, hay formas más o menos sencillas de calcular el último día del mes.

PHP

echo date('t'); // último día del mes actual

/* Ultimo día del mes de una fecha concreta */
$fecha = new DateTime('2016-02-10');
echo  $fecha->format('t'); // 29
echo $día;

En PHP es muy sencillo,podemos pasar el parámetro 't' la la función date() (utiliza la fecha del sistema), o bien, utilizar el método format() en un objeto DateTime.

C#

class Fechas {
 public static DateTime UltimoDiaMes(DateTime fechaOrigen)
 {
   DateTime fecha = new DateTime(fechaOrigen.Year, fechaOrigen.Month, 1);
   return fecha.AddMonths(1).AddDays(-1);
 }
}

En C#, lo más sencillo es crear un objeto DateTime con el primer día del mes; Luego, sumamos un mes (la fecha se "posiciona" en el día 1 del mes siguiente) y restamos un día.
Esta función de ejemplo recibe como parámetro un objeto DateTime del que partimos para hacer el cálculo.
Si queremos calcular el último día del mes actual llamamos a esta función así:

DateTime fechaUDM = Fechas.UltimoDiaMes(new DateTime());
La variable fechaUDM es un objeto DateTime que almacena al último día del mes actual.

Si sólo queremos el día, lo extraemos del objeto DateTime.
int día = Fechas.UltimoDiaMes(new DateTime()).Day;

JavaScript

var fecha = new Date();
fecha.setMonth(fecha.getMonth() + 1);
fecha.setDate(0);

En JavaScript, el método es similar a C#:
Primero, incrementamos el mes, y luego llamamos a setDate() con el valor 0; esto nos sitúa en el último día del mes anterior. Si el valor es -1 pasaríamos al penúltimo día del mes anterior, y así.

PostgreSQL

SELECT cast(date_trunc('month', current_date) + interval '1 month - 1 day' AS date)

En PostgreSQL, la función date_trunc() con el parámetro 'month' devuelve una fecha correspondiente al primer día del mes. En realidad, lo que hace es recortar la fecha al nivel / precisión que indicamos. En este caso, recorta a nivel de mes, con lo que mantiene el año y el mes del segundo parámetro. Este cálculo lo encontré aquí: PostgreSQL: Date LastDay

Una aclaración: estos ejemplos de código no me los he inventado yo, los podréis encontrar en foros o en la documentación de los distintos lenguajes, más o menos con la misma apariencia.

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