Mostrando entradas con la etiqueta Java. Mostrar todas las entradas
Mostrando entradas con la etiqueta Java. Mostrar todas las entradas

martes, 8 de diciembre de 2009

Desencriptar una cadena en Java

En prácticamente todas las aplicaciones, siempre es necesaria la encriptación de algún tipo de información. El ejemplo más simple son las contraseñas de usuario, información personal o cualquier tipo de información sensible dentro del ámbito de la aplicación.

Anteriormente, habíamos visto un mecanismo para encriptar contraseñas y convertir el texto encriptado en una cadena almacenable en una base de datos o fichero sin problemas de codificación, mediante el API estandar de Java.

Veamos ahora cómo sería el proceso inverso:

  String cadena = "WZMLNsMJeI2PDCPuWudVucLfRyQlffqA" +
                  "1yYKWLOLuCv2SzIcw0Aegh6w3o6FjQ3T";
  String semilla = "0123456789";
  String cadenaDesencriptada = null;
  String tmp = new String( ( semilla.trim().concat("99999999") ).substring(0, 8 ) );
      
  byte[] claveDesc = new BASE64Decoder().decodeBuffer( cadena );
  SecretKeySpec desKey = new SecretKeySpec( tmp.getBytes(), "DES");
      
  Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
  cipher.init(Cipher.DECRYPT_MODE, desKey);
      
  cadenaDesencriptada = new String(cipher.doFinal(claveDesc));


Tras ejecutar el anterior código, obtendríamos la cadena original "No se a quien odio más, a Batman o a Guti" dentro de la variable cadenaDesencriptada.

Evidentemente, todo el código del ejemplo habrá que incluirlo dentro del correspondiente bloque try-catch y realizar la gestión de excepciones adecuada según nuestra aplicación.

Ver artículo: Encriptar una cadena en Java

domingo, 22 de noviembre de 2009

Encriptar una cadena en Java

En prácticamente todas las aplicaciones, siempre es necesaria la encriptación de algún tipo de información. El ejemplo más simple son las contraseñas de usuario, información personal o cualquier tipo de información sensible dentro del ámbito de la aplicación.

Veamos cómo podemos codificar una cadena cualquiera utilizando el API estandar de Java.

        String claveEncriptada = null;
        String claveOriginal = "No se a quien odio más, a Batman o a Guti";
        String semilla = "0123456789";

        // Generamos una clave secreta.
        SecretKeySpec desKey = new SecretKeySpec(new String((semilla.trim().concat("99999999")).substring(0, 8)).getBytes(), "DES");
        Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, desKey);
        byte[] claveEncriptadaBytes = cipher.doFinal( claveOriginal.getBytes() );
        claveEncriptada = new BASE64Encoder().encode( claveEncriptadaBytes );






Evidentemente el código mostrado anteriormente habrá que incluirlo en un método con su generador de clave y su bloque try-catch.

Como podemos ver, la cadena que tratamos de codificar es: "No se a quien odio más, a Batman o a Guti" y el resultado que obtendríamos si ejecutaramos las instrucciones del listado sería: "WZMLNsMJeI2PDCPuWudVucLfRyQlffqA1yYKWLOLuCv2SzIcw0Aegh6w3o6FjQ3T".

La semilla, es utilizada por el algoritmo de encriptación para cifrar la cadena. Si quisieramos desencriptar la cadena obtenida, necesitaríamos la semilla para poder hacerlo. Es como la llave que abre la caja fuerte que contiene el contenido desencriptado original. A la hora de realizar una aplicación es de suma importancia que cada dato que encriptemos tenga una semilla diferente para no comprometer el total de la información en el caso de que la misma sea descubierta. El mecanismo para la elección debe ser elegido cuidadosamente para dificultar el trabajo a un posible "ladrón" que quisiera acceder a la información.

Es importante tener en cuenta que la encriptación genera una cadena de bytes, los cuales, transformados en una cadena de carácteres podría implicar la existencia de carácteres extraños en la misma, cosa que nos podría dar problemas a la hora de almacenar la información encriptada en una base de datos si no tenemos en cuenta que podemos insertar dicho tipo de cadenas a la hora de almacenarlas en fichero o base de datos. Para evitar dicho problema, utilizamos el codificación BASE64.

Ver artículo: Desencriptar una cadena en Java

Gracias a Gomstor por encontrar un error en el código que ya ha sido corregido.

miércoles, 18 de marzo de 2009

Parámetros en línea de comandos en java

Una de las cosas más engorrosas a la hora de preparar una pequeña aplicación java en línea de comandos,al igual que en otros lenguajes de programación, es el tener que controlar todos los parámetros que se le pasan a la aplicación, el orden de los mismos, que vayan separados al estilo -s -l -r o -slr...

Existe un proyecto de Martian Software Inc. llamado JSAP, que será de gran ayuda para este tipo de cosas. Lo descubrí casi de casualidad y me parece una maravilla. Pero veamos un ejemplo: Imaginemos un programa que lee de un archivo una serie de datos, los procesa, y los escribe en un archivo de salida, exceptuando los datos que generan alguna incidencia que son grabados en un tercer archivo de errores, la aplicación además parará cuando se produzcan un número de errores igual al indicado en otro parámetro, es decir el clásico problema de metodología Warnier.

El programa debería ejecutarse con un comando del estilo de:

java com.trapallada.programa -i entrada.txt -o salida.txt -e errores.txt -n 500


donde los parámetros podrían ser aparecer en cualquier orden.

/*
* Por CNG (www.trapallada.com).
*/
public static void main(String[] args)
{
try
{
JSAP jsap = new JSAP();

// Se crean los diferentes parámetros:
// Parámetro del fichero de entrada
FlaggedOption optEntrada = new FlaggedOption("Fichero de Entrada")
.setStringParser(JSAP.STRING_PARSER)
.setRequired(true)
.setShortFlag('i')
.setLongFlag("input");

// Parámetro del fichero de salida
FlaggedOption optSalida = new FlaggedOption("Fichero de Salida")
.setStringParser(JSAP.STRING_PARSER)
.setRequired(true)
.setShortFlag('o')
.setLongFlag("output");

// Parámetro del fichero de errores
FlaggedOption optErrores = new FlaggedOption("Fichero de Errores")
.setStringParser(JSAP.STRING_PARSER)
.setRequired(true)
.setShortFlag('e')
.setLongFlag("errores");

// Parámetro del fichero de número de errores (no obligatorio)
FlaggedOption optNumeroErrores = new FlaggedOption("Número de Errores")
.setStringParser(JSAP.INTEGER_PARSER)
.setRequired(false)
.setShortFlag('n')
.setLongFlag("numErrores");

// Registramos los parámetros
jsap.registerParameter(optEntrada);
jsap.registerParameter(optSalida);
jsap.registerParameter(optErrores);
jsap.registerParameter(optNumeroErrores);

// Se procesan los argumentos de la línea de comandos
JSAPResult config = jsap.parse(args);

// Si se produce algún error generamos el mensaje de error con JSAP.
if (!config.success())
{
System.err.println();
System.err.println("Uso: java " + Lector.class.getName());
System.err.println(" " + jsap.getUsage());
System.err.println();
System.exit(1);
}

// Se recogen los datos del la configuración cargada desde los
// parámetros de la línea de comandos.
String entrada = config.getString(optEntrada.getID());
String salida = config.getString(optSalida.getID());
String errores = config.getString(optErrores.getID());
Integer numErrores = new Integer(config.getInt(optNumeroErrores.getID()));
}
catch (Exception e)
{
e.printStackTrace();
}
}

Esto sería lo que devolvería la ejecución de la clase en el caso de no poner nada a continuación de la llamada a la misma (sin parámetros).

Uso: java com.trapallada.programa
(-i|--input) <Fichero de Entrada> (-o|--output) <Fichero de Salida> (-e|--errores) <Fichero de Errores> [(-n|--numErrores) <Número de Errores>]

jueves, 19 de febrero de 2009

Ordenar mis fotos por carpetas

Siempre he tenido un serio problema a la hora de organizar las fotografías en mi disco duro. Al principio volcaba todo en una misma carpeta, cosa que en cuestión de pocas descargas de la cámara, convertía la carpeta en algo totalmente invisitable. El segundo paso fue el crear carpetas con nombres descriptivos y guardar en ellas las fotografías, pero aún así la cosa era un infierno.

Las aplicaciones como Picasa, ACD See, PhotoMesa permiten gestionar bastante bien las fotografías, pero a mi, como buen usuario proviniente del MS-DOS al que le gusta saber dónde están las cosas en mi disco duro, y no perder el control sobre él, no acababan de convencerme.

Tras una breve incursión en el mundo del Linux, descubrí la aplicación f-Spot, que realizaba una clasificación en carpetas de las fotografías en función de los datos Exif de los archivos JPG.

Ya de vuelta al mundo Windows, y tras decantarme por Picasa, principalmente por su facilidad de uso (no para mi, sino para mi mujer), me encontré con mi eterno problema de clasificar las fotografías. Por lo que en ese momento me decidí a crear mi propio programita Java que clasifique en carpetas las fotografías a partir de sus datos Exif.

Lo primero de todo es obtener las fotografías de origen a partir de una ruta:

/*
* Por CNG (www.trapallada.com).
*/

/**
* Devuelve una colección con todos los ficheros JPG de una carpeta.
* Para obtener esa colección recorre recursivamente todo el árbol de
* directorios bajo la carpeta indicada.
* @param f File indicando la carpeta en la que realizar la búsqueda recursiva.
* @return Una Colección con todos los ficheros baja la carpeta pasada al método.
*/
private static Collection getFiles( File f ) {
Collection c = new ArrayList();
if ( f.isDirectory() ) {
File[] listOfFiles = f.listFiles();
for (int i = 0; i < listOfFiles.length; i++) {
c.addAll( getFiles( listOfFiles[i]) );
}
} else {
JFileChooser chooser = new JFileChooser();
if ( chooser.getTypeDescription(f).equals("Archivo JPG") ) c.add(f);
}
return c;
}

A partir de la colección de ficheros JPG, y mediante la librería metadata-extractor, yo he usado la versión 2.3.1, podemos generar un nuevo nombre de fichero y mover el archivo original de la colección obtenida a su destino definitivo.

// Por CNG (www.trapallada.com).
try {
String salida = "D:/Mis documentos/Mis imágenes/preImportado";
String entrada = "D:/CarpetaOrigen";

Collection files = getFiles( new File(entrada) );

Iterator itFiles = files.iterator();
while ( itFiles.hasNext() ) {
File ficheroJpg = (File) itFiles.next();
try {
Metadata metadata = JpegMetadataReader.readMetadata( ficheroJpg );
Directory exif = metadata.getDirectory( Class.forName("com.drew.metadata.exif.ExifDirectory") );

Calendar fecha = Calendar.getInstance();
try {
fecha.setTime( exif.getDate( ExifDirectory.TAG_DATETIME_ORIGINAL ) );
File toFile = new File( salida + "/" + fecha.get( Calendar.YEAR ) + "-" +
Formateador.fill( new Integer( fecha.get( Calendar.MONTH ) + 1 ).toString(),
'0', 2, Formateador.IZQUIERDA ) + "/" +
ficheroJpg.getName() );
Ficheros.moveFile( ficheroJpg, toFile);
}
catch ( MetadataException j ) {
System.out.println("ERROR: " + ficheroJpg.getName() );
}
} catch ( JpegProcessingException e ) {
e.printStackTrace();
}
}
}
catch ( Exception e )
{
e.printStackTrace();
}
}

Nota: El método Formateador.fill tiene el siguiente javadoc:
/**
* M&amp;amp;amp;amp;eacute;todo que devuelve la cadena rellena con el caracter "caracterRelleno", hasta
* ocupar "longitud" posiciones, desde el lado indicado.
*
* @param cadena: Cadena a ser formateada
* @param caracterRelleno: Caracter con el que se va a rellenar
* @param longitud: Indica la longitud de la cadena resultante
* @param lado: nos indica en que lado situar los caracteres de Relleno
* @return Cadena rellena
*/
Nota 2: No creo que haga falta explicar lo que hace el método Ficheros.moveFile.

jueves, 12 de febrero de 2009

Obtener los campos SERIAL de Informix tras un INSERT



Problema:

Tras hacer un INSERT en una tabla de Informix que contiene campos del tipo SERIAL nos encontramos con que tras realizarlo, tenemos todos los datos del registro excepto los autogenerados por la base de dato.

Solución:

// Por CNG (www.trapallada.com).
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;

String queryString = "INSERT INTO " + TABLA + " (" + CAMPOS +
") VALUES ( ?,?,?,? )";
preparedStatement = conn.prepareStatement(queryString,
Statement.RETURN_GENERATED_KEYS);

int i = 1;
// El primer campo de la tabla es SERIAL
preparedStatement.setInt(i++, 0);
preparedStatement.setString( i++, vo.getCampo2() );
preparedStatement.setString(i++, vo.getCampo3() );
preparedStatement.setString(i++, vo.getCampo4() );

int insertedRows = preparedStatement.executeUpdate();
resultSet = preparedStatement.getGeneratedKeys();

resultSet.next();
vo.setCampo1( resultSet.getInt(1) );

En el momento de crear el PreparedStatement incluimos el parámetro Statement.RETURN_GENERATED_KEYS para indicarle a la aplicación que recupere los valores de los SERIAL generados por la base de datos.

Tras ejecutar el INSERT, se recupera un ResultSet con los campos SERIAL generados con el método getGeneratedKeys() del PreparedStatement.

Tras eso, accedemos al ResultSet para obtener los valores.