Si queremos mantener algunos datos ocultos, si no queremos que sean legibles a simple vista entonces debemos encriptarlos.

Encriptar significa convertir un texto legible en un texto completamente ilegible. Técnicas para encriptar existen al menos desde la época del general romano Julio César; ahora tenemos la gran ventaja de que podemos usar computadoras consiguiendo así algoritmos mucho más poderosos y miles o millones de veces más rápidos que hacerlo manualmente.

Un stored procedure para encriptar y desencriptar datos

Este stored procedure te permitirá encriptar o desencriptar cualquier texto que desees, la única condición es que su longitud debe ser menor o igual a 32765 caracteres. Esa no es una limitación del algoritmo sino de la implementación del tipo de datos VARCHAR en Firebird que solamente nos permite tener como máximo esa cantidad de caracteres.

Pero 32765 caracteres es más que suficiente para la inmensa mayoría de los casos. Si alguna vez necesitas encriptar textos más largos entonces deberás modificar el stored procedure para usar el tipo de datos BLOB de texto en lugar de VARCHAR.

CREATE PROCEDURE ENCRIPTAR(
   tcTexto              VARCHAR(32765),
   tcAccion             CHAR(1),
   tcNumeroEncriptacion VARCHAR(255),
   tcNumeroRepeticion   VARCHAR(255))
RETURNS(
   ftcNuevoTexto VARCHAR(32765))
AS
   DECLARE VARIABLE lnI          SMALLINT;
   DECLARE VARIABLE lnJ          SMALLINT;
   DECLARE VARIABLE lnK          SMALLINT;
   DECLARE VARIABLE lcCaracter   CHAR(1);
   DECLARE VARIABLE lnAscii      SMALLINT;
   DECLARE VARIABLE lnValor1     SMALLINT;
   DECLARE VARIABLE lnValor2     SMALLINT;
   DECLARE VARIABLE lnNuevoAscii SMALLINT;
BEGIN

   ftcNuevoTexto = '';     -- El texto que se devolverá

   lnI = 1;
   lnJ = 1;
   lnK = 1;

   WHILE (lnI <= CHAR_LENGTH(tcTexto)) DO BEGIN
      lcCaracter    = SUBSTRING(tcTexto FROM lnI FOR 1);     -- Obtiene el caracter que está en la posición lnI
      lnAscii       = ASCII_VAL(lcCaracter);                  -- Halla el código ASCII del caracter
      lnValor1      = CAST(SUBSTRING(tcNumeroEncriptacion FROM lnJ FOR 1) AS SMALLINT);
      lnValor2      = CAST(SUBSTRING(tcNumeroRepeticion FROM lnK FOR 1) AS SMALLINT);
      lnNuevoAscii  = MOD((lnAscii + IIF(tcAccion = 'E', 1, -1) * lnValor1 * lnValor2), 256);
      lnNuevoAscii  = lnNuevoAscii + IIF(lnNuevoAscii < 0, 256, 0);
      ftcNuevoTexto = ftcNuevoTexto || ASCII_CHAR(lnNuevoAscii);
      lnI = lnI + 1;
      lnJ = lnJ + 1;
      lnJ = IIF(lnJ > CHAR_LENGTH(tcNumeroEncriptacion), 1, lnJ);
      lnK = lnK + 1;
      lnK = IIF(lnK > CHAR_LENGTH(tcNumeroRepeticion), 1, lnK);
   END

   SUSPEND;

END;

Escribir el algoritmo fue muy sencillo pero su efecto es muy poderoso. Para probar cuan efectivo es puedes encriptar texto con él y luego dárselo a quienes estén interesados en desencriptarlo. Lo más probable es que todos se rindan al cabo de unos días al no conseguir el objetivo de desencriptarlo.

Recibe como parámetros de entrada el texto que se quiere encriptar, la acción a realizar con ese texto (si es “E” significa que se quiere encriptarlo, si es “D” significa que se quiere desencriptarlo), un número de encriptación y un número de repetición.

Se devuelve el resultado de encriptar o desencriptar el texto.

Si quieres leer una explicación del algoritmo puedes entrar en esta página:

https://firebird21.wordpress.com/2014/01/28/bases-de-datos-shareware-3/

Un stored procedure para verificar la encriptación

¿Es el algoritmo suficientemente bueno? ¿Sirve tanto para encriptar como para desencriptar texto?

Para comprobarlo aquí hay otro stored procedure, el cual primero encripta un texto y luego lo desencripta. Si el algoritmo está ok entonces el texto desencriptado debe ser exactamente igual al texto original.

CREATE PROCEDURE VERIFICARENCRIPTACION
RETURNS(
   ftcResultado VARCHAR(32765))
AS
   DECLARE VARIABLE lcTexto              VARCHAR(32765);
   DECLARE VARIABLE lcNumeroEncriptacion VARCHAR(32765);
   DECLARE VARIABLE lcNumeroRepeticion   VARCHAR(32765);
BEGIN

   lcTexto = 'FIREBIRD SQL ES BUENÍSIMO Y ADEMÁS ES SIEMPRE GRATIS. Y ESO ES GENIAL.29/01/2014*01/03/2014*29/01/2014';

   lcNumeroEncriptacion = '93753468';
   lcNumeroRepeticion = '7915317786';

   ftcResultado = (SELECT ftcNuevoTexto FROM ENCRIPTAR(:lcTexto, 'E', :lcNumeroEncriptacion, :lcNumeroRepeticion));

   SUSPEND;

   ftcResultado = (SELECT ftcNuevoTexto FROM ENCRIPTAR(:ftcResultado, 'D', :lcNumeroEncriptacion, :lcNumeroRepeticion));

   SUSPEND;

END;

Y aquí está el resultado de ejecutar el stored procedure anterior. Como puedes ver funciona perfectamente; primero encriptó (hizo ilegible) al texto que se le envió como parámetro de entrada y luego desencriptó ese resultado para volver a mostrar el texto original.

ENCRIPTAR1

Captura 1. Si haces clic en la imagen la verás más grande

¿Aún no estás seguro de su efectividad? Puedes darle el texto que se encuentra en la primera fila a todas las personas que quieras, inclusive diciéndoles que en alguna parte está el número 2014, por ejemplo, para darles una ayuda. Y luego espera sentado a que alguien obtenga la segunda fila.

Hay muchas formas de mejorar el algoritmo, una de ellas es agregarle cada “n” caracteres otro caracter. Por ejemplo, cada 3 caracteres le agregas un caracter al azar. Al desencriptar debes ignorar a los caracteres que se encuentran en las posiciones 3, 6, 9, 12, 15, etc., todos los que sean múltiplos de 3. Eso hará que el texto encriptado sea un tercio más largo que el texto original. Si no quieres insertar un caracter al azar cada “n” posiciones entonces puedes tener un número que te indique donde debes insertar el caracter al azar. Por ejemplo si a la variable la llamas lcCaracteresAdicionales con el valor ‘7,8,13,19,24,31,43,44,45,60’ eso significa que los caracteres que se encuentran en las posiciones 7, 8, 13, 19, 24, 31, 43, 44, 45, 60 deben ser ignorados porque están solamente de relleno.

Cuanto más largos sean lcNumeroEncriptacion y lcNumeroRepeticion más efectivo y poderoso será el algoritmo. Las secuencias se repiten cada CHAR_LENGTH(lcNumeroEncriptacion) * CHAR_LENGTH(lcNumeroRepeticion). En nuestro ejemplo cada 8 * 10, o sea cada 80 caracteres. Si las longitudes de lcNumeroEncriptacion y lcNumeroRepeticion fueran de 25 y 30 respectivamente, entonces se repetirían cada 25 * 30 = 750 caracteres. Con longitudes suficientemente largas puedes hacer que se repitan cada varios miles de caracteres, complicándoles muchísimo la vida a los curiosos.

Lo bueno de este algoritmo es que al 99.99% de las personas que ven la primera fila ya les da pereza intentar obtener la segunda fila luego de unos cuantos minutos de intentarlo (eso, si alguna vez lo intentan).

IMPORTANTÍSIMO

Algo que jamás debes olvidar son los valores que usaste en los parámetros de entrada tcNumeroEncriptacion y tcNumeroRepeticion porque si los olvidas lo más probable es que jamás puedas volver a obtener el texto original. Así que mucho, pero mucho cuidado con eso.

NOTA:

Aunque aquí el algoritmo para encriptar y desencriptar textos se mostró en un stored procedure lo normal es que lo tengas como una función del usuario en tu lenguaje de programación (Visual FoxPro, Visual Basic, Delphi, C, C++, Java, etc.)

Artículos relacionados:

Bases de datos shareware (3)

El índice del blog Firebird21