jueves, junio 24, 2010

Encriptación de datos desde aplicación.

En todos los ejemplos que he encontrado sobre encriptación y desencriptación de datos en Oracle, siempre se usan procedimientos PL/SQL para establecer la seguridad en la base de datos. No he encontrado un sólo ejemplo que permita hacer un insert "encriptado" y una consulta "desencriptada".

Imaginando el siguiente escenario: Cada usuario tiene una "palabra secreta" para desencriptar su propia información. En la base de datos todo se registra encriptado.

Para ello, Oracle ofrece dos paquetes:

- DBMS_OBFUSCATION_TOOLKIT. A partir de Oracle8i, que soporta encriptación DES y triple DES (Data Encription Standard), y con ciertas limitaciones (por ejemplo, los datos a encriptar han de ser un múltiplo de 8 bytes).

- DBMS_CRYPTO. A partir de Oracle10g. Soporta más formas de encriptación, como la AES (Advanced Encription Standard), que sustituye el anterior DES y no hay limitación con el número de carácteres.

Para mas información, la documentación de Oracle ofrece esta comparativa de funcionalidades.

Uso de DBMS_OBFUSCATION_TOOLKIT

El siguiente ejemplo muestra la encriptación de la palabra "SECRETO" (8 bytes) y genera un error al intentar encriptar "SECRETITOS!" (11 bytes)

SQL> select DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>'SECRETO!',key_string=>'clavedesencript') from dual;

DBMS_OBFUSCATION_TOOLKIT.DESENCRYPT(INPUT_STRING=>'SECRETO!',KEY_STRING=>'CLAVED
--------------------------------------------------------------------------------
lr??


SQL> select DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>'SECRETITOS!',key_string=>'clavedesencript') from dual;
select DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>'SECRETITOS!',key_string=>'clavedesencript') from dual
*
ERROR at line 1:
ORA-28232: invalid input length for obfuscation toolkit
ORA-06512: at "SYS.DBMS_OBFUSCATION_TOOLKIT_FFI", line 21
ORA-06512: at "SYS.DBMS_OBFUSCATION_TOOLKIT", line 126



El error ORA-28232 corresponde a la longitud inadecuada de la cadena a encriptar. 'SECRETITOS!" tiene 11 carácteres y el paquete está limitado a múltiplos de 8 bytes. Por ejemplo, el número de una tarjeta de crédito.

SQL> select DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>'1234567812345678',
2 key_string=>'clavedesencript')
3 from dual;

DBMS_OBFUSCATION_TOOLKIT.DESENCRYPT(INPUT_STRING=>'1234567812345678',KEY_STRING=
--------------------------------------------------------------------------------
}??X??



De modo que la desencriptación funciona de igual modo, usando la función DESDecrypt

SQL> select DBMS_OBFUSCATION_TOOLKIT.DESDecrypt(
2 input_string=>DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>'SECRETO!',key_string=>'CLAVE_BUENA'),
3 key_string=>'CLAVE_BUENA') from dual;

DBMS_OBFUSCATION_TOOLKIT.DESDECRYPT(INPUT_STRING=>DBMS_OBFUSCATION_TOOLKIT.DESEN
--------------------------------------------------------------------------------
SECRETO!



SQL> select DBMS_OBFUSCATION_TOOLKIT.DESDecrypt(
2 input_string=>DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>'1111222233334444',key_string=>'CLAVE_BUENA'),
3 key_string=>'CLAVE_BUENA') from dual;

DBMS_OBFUSCATION_TOOLKIT.DESDECRYPT(INPUT_STRING=>DBMS_OBFUSCATION_TOOLKIT.DESEN
--------------------------------------------------------------------------------
1111222233334444



y si se utiliza una clave distinta, la información no se desencriptará adecuadamente.

SQL> select DBMS_OBFUSCATION_TOOLKIT.DESDecrypt(
2 input_string=>DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>'SECRETO!',key_string=>'CLAVE_BUENA'),
3 key_string=>'CLAVE_ERRONEA') from dual;

DBMS_OBFUSCATION_TOOLKIT.DESDECRYPT(INPUT_STRING=>DBMS_OBFUSCATION_TOOLKIT.DESEN
--------------------------------------------------------------------------------
???! k

...ó producirá un error.


SQL> select DBMS_OBFUSCATION_TOOLKIT.DESDecrypt(
2 input_string=>DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>'1111222233334444',key_string=>'CLAVE_BUENA'),
3 key_string=>'CLAVE_MALA') from dual;
ERROR:
ORA-29275: partial multibyte character

no rows selected




Uso de DBMS_CRYPTO

El siguiente ejemplo muestra la encriptación de la palabra "SECRETITOS!" (11 bytes) usando una suite de encriptación que ya viene implementada. En concreto es la DES_CBC_PCKS5, que contiene encriptación DES, encadenamiento de cifrado de bloques y modificadores de relleno PCKS5.

Es preciso, para invocar correctamente a este paquete, realizar una conversión a RAW de las cadenas a encriptar. He utilizado para ello el paquete UTL_RAW y la función UTL_I18N.STRING_TO_RAW.


SQL> select DBMS_CRYPTO.ENCRYPT(src => UTL_I18N.STRING_TO_RAW ('SECRETITOS!', 'AL32UTF8'),
2 typ => 4353,
3 key => UTL_I18N.STRING_TO_RAW ('clavedesencript', 'AL32UTF8')
4 )
5 from dual;

DBMS_CRYPTO.ENCRYPT(SRC=>UTL_I18N.STRING_TO_RAW('SECRETITOS!','AL32UTF8'),TYP=>4
--------------------------------------------------------------------------------
1BA7F933C2CAD0C7F4FDA685775BE0E7



Y la desencriptación de la información, con la función DECRYPT.

SQL> select UTL_RAW.cast_to_varchar2(
2 DBMS_CRYPTO.DECRYPT(
3 DBMS_CRYPTO.ENCRYPT(src => UTL_I18N.STRING_TO_RAW ('SECRETITOS!', 'AL32UTF8'),
4 typ => 4353,
5 key => UTL_I18N.STRING_TO_RAW ('clavedesencript', 'AL32UTF8')
6 ),
7 typ => 4353,
8 key => UTL_I18N.STRING_TO_RAW ('clavedesdecript', 'AL32UTF8')
9 )
10 )
11 from dual;

UTL_RAW.CAST_TO_VARCHAR2(DBMS_CRYPTO.DECRYPT(DBMS_CRYPTO.ENCRYPT(SRC=>UTL_I18N.S
--------------------------------------------------------------------------------
SECRETITOS!



Para más información sobre las múltiples formas de encriptación y uso de claves, lo mejor es consultar la documentación del paquete DBMS_CRYPTO.

12 comentarios:

Sergio Luna dijo...

Muchas gracias por tu ayuda!!!

Sigue así con este magnífico blog.

Un saludo

Javier Morales dijo...

Gracias a ti, Sergio!

Un abrazo,

Anónimo dijo...

Hola Javier, Cordial Saludo. Excelente tu explicacion y gracias por ello. Tengo una inquietud: utilizando DBMS_CRYPTO puedo controlar la longitud de la cadana encriptada ? Cordialmente, Jesus Franco.

Anónimo dijo...

Hola Javier, gracias por tu ejemplo, es excelente. Tengo una inquietud al respecto, es posible controlar la longitud de la cadena encriptada que retorna DBMS_CRYPTO.ENCRYPT ? o como puedo determinar el largo de la cadana ecriptada. Gracias de nuevo. Jesus Franco

Javier Morales dijo...

Hola Jesús,

El paquete de encriptación retorna un tipo RAW, que es de longitud fija.

:)

Anónimo dijo...

El uso de DBMS_CRYPTO requiere de una licencia adicional?

Javier Morales dijo...

Hola Anónimo,

No hace falta licencia adicional. Es un paquete suministrado de PL/SQL como DBMS_OUTPUT o UTL_FILE.

Consulta con tu DBA si tienes algún problema al ejecutarlo, porque quizás tenga que concederte permisos para usarlo.

Un saludo

William dijo...

Hola Javier, se puede utilizar decode o case when en el where de un select,
select * from tabla1 where case when(tabla1.campo1=.t.) then ... else ... end

Javier Morales dijo...

Hola William,

Si.

Doc. Oracle sobre Case.

Un saludo,
Javier

Víctor López dijo...

Perfecto,

es exactamente lo que estaba buscando. Y como seguimos utilizando Oracle 10 no hay ningún problema por que la entrada sea de hace cinco años ;-).

Saludos.

Unknown dijo...
Este comentario ha sido eliminado por el autor.
Javier Morales dijo...

Hola Víctor!

Muchas gracias por tu comentario!
Solo añadir que el paquete DBMS_CRYPTO se sigue usando en Oracle12c! :)

Un saludo,
Javier