Cómo Manejar el TrustStore de Java sin Volverse Loco

KeyStore Explorer 5.1

KeyStore Explorer 5.1

Para trabajar con certificados SSL, key stores y trust stores las herramientas de línea de comando son bastante áridas. Por suerte existe KeyStore Explorer. Me animaría a decir que tiene todo lo necesario, pero lo más importante es que es muy fácil de usar, tiene ayuda integrada y muchas opciones.

Muy poca gente menciona esta herramienta cuando se trata de orientar a la gente que tiene que hacer alguna operación con claves o certificados SSL, pero es muy recomendable.

Principalmente convierte en transparente un proceso que suele ser bastante opaco si no se sabe bien lo que se está haciendo con la herramienta keytool.

Hashes y Ataques de Denial of Service: Java, Python, Ruby, PHP y ASP

En casi todos los lenguajes de programación hay algún tipo de estructura de datos como una tabla hash (llamados por ejemplo hash map, hash table, dictionary, entre otros). Básicamente permiten asociar un valor a una clave y posteriormente obtener dicho valor proporcionando la clave en un tiempo constante sin importar cuantas claves haya en la estructura.

Para lograr que la búsqueda sea en tiempo constante, los valores se almacenan en una posición de la tabla según el valor de hash de su clave.

Es posible, aunque poco probable, que dos claves distintas generen una colisión, es decir que dos claves distintas generen el mismo valor de hash y por lo tanto dos valores deban guardarse en una misma posición. Una forma de hacer eso es tener una lista de valores en lugar de solo un valor en cada posición. Luego de identificar la posición mediante el valor de hash se hace una búsqueda secuencial entre todos los elementos de esa lista.

Un ataque de denegación de servicio mediante hash o Hash DoS consiste en explotar esas colisiones para hacer que la lista de valores sea muy larga y se requiera mayor tiempo de procesamiento para buscar, agregar y eliminar elementos.

En lenguajes usados para aplicaciones Web como PHP, Python, Java, Ruby o ASP, los valores ingresados mediante un formulario web son cargados en tablas hash para su posterior procesamiento y por eso todos resultaron vulnerables a esos ataques.

En esta transparencia de PHP se muestra un requerimiento POST creado para explotar esta vulnerabilidad: http://talks.php.net/show/phpuk2012/14

Existen tres formas de atacar el problema, con diferentes grados de éxito y complicación.

  • PHP, ColdFusion y ASP optaron por “controlar los daños” limitando a 1.000 la cantidad de valores de entrada que pueden llegar en un requerimiento POST. En el php.ini de PHP 5.3.9 en adelante hay una configuración max_input_vars=1000.
  • Python [http://bugs.python.org/issue13703], Ruby y Java 7 adoptaron una mejora en el algoritmo de hash para dificultar la generación de colisiones incorporando números pseudoaleatorios en su cálculo.
  • Java 8 incorpora un árbol balanceado para almacenar todos los valores cuyas claves tengan el mismo hash [http://openjdk.java.net/jeps/180]. De esa forma con miles de millones de valores, apenas requeriría 50 operaciones para resolver la búsqueda.

Es importante destacar que la solución adoptada por PHP, ASP y ColdFusion de limitar la longitud de la entrada no resuelve por completo el problema ya que por ejemplo si se envía un hash con muchas colisiones dentro de un string JSON, pueden ser menos de 1.000 parámetros e igual disparar el problema. Básicamente las aplicaciones deben igualmente verificar internamente los datos que reciben.

La alternativa de mejorar la función de hash es un bastante más robusta, aunque puede ser más difícil de implementar. En algunos lenguajes podría tener un impacto negativo en la performance o requerir cambios en algún comportamiento ya definido.

La solución elegida en Java 8 (y que vuelve atrás los cambios incorporados en Java 7 para tratar este mismo problema) es muy robusta y elimina la causa fundamental del problema.

TIFF vs. PNG vs. JPEG 2000: Compresión de Imágenes sin Pérdida

Cuando se decide digitalizar fotos para conservarlas lo ideal es guardarlas en un formato sin pérdida de información para retener el detalle tal como se pudo obtener del escáner. La desventaja es que ocupan mucho más espacio que un JPEG.

Muchos prefieren directamente un formato sin compresión como TIFF sin comprimir, pero existen formatos de compresión sin pérdida que permiten ahorrar espacio de almacenamiento sin un costo significativo.

El formato TIFF admite diferentes tipos de compresión, luego están el PNG y el JPEG 2000.

Para comparar armé un grupo de 356 imágenes color de 24 bits y aproximadamente de 3.200 por 2.000 pixeles que totalizan 6.164.301.370 bytes sin comprimir (TIFF sin compresión).

Las comprimí a

  • JPEG 2000 sin pérdida,
  • a PNG
  • a TIFF con compresión LZW
  • y a TIFF con compresión Zip.

Estos fueron los comandos que usé.

JPEG 2000: mogrify -format jp2 -define jp2:rate=1.0 -define jp2:mode=int -depth 8 -define jp2:tilewidth=256 -define jp2:tileheight=256 -path /home/user/jp2-lossless/ /home/user/test/*.tif

TIFF LZW: mogrify -format tif -depth 8 -compress LZW -path /home/user/tiff-lzw/ /home/user/test/*.tif

TIFF Zip: mogrify -format tif -depth 8 -compress Zip -path /home/user/tiff-lzw/ /home/user/test/*.tif

PNG: mogrify -depth 8 -path /home/user/png-9/ -format png -quality 9 /home/user/test/*.tif

Los resultados.

Formato Bytes MB % del MAX % del MIN
TIFF Zip 3.142.359.890 2.996,79 50,98% 118,91%
PNG 9 2.750.377.023 2.622,96 44,62% 104,07%
JPEG 2000 2.642.693.320 2.520,27 42,87% 100,00%
TIFF LZW 3.520.584.906 3.357,49 57,11% 133,22%
TIFF sin comprimir 6.164.301.370 5.878,74 100,00% 233,26%

JPEG 2000 da la máxima compresión sin pérdida, aunque PNG se acerca muchísimo. JPEG 2000 permite ahorrar tan sólo un 1,75% más que PNG.

En un análisis foto por foto PNG comprimió más que JPEG 2000 en 36 casos y menos en 320 casos.

Si se incorporan al análisis otros factores como disponibilidad de herramientas, soporte del formato a futuro y el tiempo de compresión y descompresión, hay que destacar algunas cuestiones.

  • No todas las herramientas leen todas las combinaciones de TIF con sus diferentes formatos de compresión. Es por eso que la Biblioteca del Congreso y otras instituciones recomiendan sólo usar TIFF sin comprimir para fotos.
  • JPEG es todavía un formato nuevo y por lo tanto las herramientas son nuevas. Es común encontrar problemas con algunos archivos o con la disponibilidad de herramientas en algunas plataformas. El soporte debería mejorar con el tiempo, pero al ser un formato complejo el avance es lento.
  • JPEG 2000 requiere cálculos más complejos y por lo tanto requiere mayor cantidad de procesamiento para comprimir y descomprimir las imágenes. Las distintas variantes de TIFF y PNG no requieren tanto procesamiento y son más rápidas.
  • PNG es un estándar ISO público y está respaldado por la W3C que es la organización que rige los estándares de la Web y está implementado en todos los navegadores (incluso en los móviles).
  • TIFF es un estándar público, pero no es ISO sino que es un estándar de Adobe (los dueños del Photoshop). Tiene muchas implementaciones y extensiones no estandarizadas.
  • JPEG 2000 también es un estándar ISO, pero incorpora varias patentes que podrían implicar un costo por su uso en algún momento. En cambio PNG no está alcanzado por patentes.

Cuál es el Mejor Formato para Guardar Fotos Escaneadas

La mayoría de la gente ya usa cámaras de fotos digitales. Típicamente los últimos 5 o 10 años de historia fotográfica familiar son totalmente digitales. Las ventajas de los archivos digitales están muy claras por eso para algunos resulta interesante digitalizar las fotos familiares anteriores a la revolución digital (digamos todas las fotos sacadas entre 1840 y 2005).

Un documento en papel (fotos, libros, documentos, etc.) dura décadas o hasta siglos sin mayor deterioro, pero un archivo digital requiere de herramientas tecnológicas para poder acceder a él y estas se vuelven obsoletas mucho más rápido por lo que hay que ser muy cuidadoso a la hora de guardar nuestros recuerdos digitales.

Bajo el concepto de preservación digital se encuadran las técnicas, herramientas y estrategias tendientes a asegurar que los recursos digitales puedan ser accedidos en el futuro. Uno de los aspectos es la elección del formato de imagen.

La Biblioteca del Congreso de Estados Unidos tiene disponible mucha información para ayudar a que la gente preserve sus documentos digitales con éxito.

http://www.digitalpreservation.gov/multimedia/videos/personalarchiving_spanish.html

También publican los criterios que utilizan dentro de su organización para seleccionar los formatos de archivos digitales. Lo mismo hacen muchas otras organizaciones como National Archives of Australia, Library and Archives Canada, Bancroft libary o Florida Digital Archive.

Entonces, ¿cuál es el mejor formato para guardar fotos escaneadas? PNG (Portable Network Graphics). Los motivos:

En general el único otro formato ampliamente aceptado, incluso más que PNG es TIFF, pero con la salvedad que sólo aceptan TIFF sin comprimir. Ninguna versión de TIFF comprimido es aceptada porque existen muchas implementaciones de TIFF y no todas igualmente adoptadas o estandarizadas.

PNG tiene la ventaja de la compresión sin pérdida sobre el TIFF. Hice una comparación de la compresión de TIFF con Zip, TIFF con LZW y JPEG 2000 con PNG donde el último comprime apenas un 1,75% menos que JPEG 2000 pero mucho más que los otros.

Cómo Grabar BD-R en Ubuntu

Si sos un afortunado poseedor de una grabadora de Blu ray y querés grabar en Ubuntu, habrás notado que con las herramientas que trae de fábrica no se puede. Pero por suerte con algunos ajustes se puede grabar BD-R perfectamente.

El primer problema es que las herramientas de grabación que vienen con Ubuntu (y Debian y muchas otras distribuciones) llamadas CDRKit no soportan grabar BD-R así que hay que eliminarlas e instalar el paquete cdrecord desde este repositorio personal:

https://launchpad.net/~brandonsnider/+archive/cdrtools

Hecho eso, debemos instalar K3b que es una interfaz amigable para grabar discos y que configuraremos para usar cdrecord y poder grabar BD-R.

Primero hay que habilitar en las opciones avanzadas “Show advanced GUI elements”

k3b-1

Luego asegurarnos que K3b esté configurado para usar cdrecord y no otra herramienta.

k3b-2

Al empezar a grabar se debe elegir también cdrecord en la opción Writting app del diálogo

k3b-3

Y elegir la opción No Multisession

k3b-4

Hecho esto deberíamos poder grabar un BD-ROM sin errores. Lo que hay que revisar es que los archivos quepan en el disco según el espacio libre.

El BD-R ya es la Opción más Económica para Almacenamiento

Disco GiB $/unidad $/GiB
Blue-Ray Disc DL 46,61 $40,00 $0,86
Blue-Ray Disc 23,31 $10,00 $0,43
DVD+R DL 7,96 $11,00 $1,38
DVD-R 4,38 $3,50 $0,80
Finalmente el Blu Ray se ha transformado en la alternativa más económica para almacenamiento óptico. Sólo considerando el costo del disco virgen el BD-R de 24 GB está a la mitad de precio de sus competidores. El BD-R DL de 50 GB está casi al mismo nivel. Si se agrega al análisis el costo de las cajas y el espacio que ocupan los discos una vez grabados, la balanza se inclina todavía más a favor del Blu Ray.Si bien no tengo una muestra de precios representativa los DVD+R DL y los DVD-R deberían costar $3,50 y $1,90 respectivamente para igualarlo en costo por GiB al BD-R de $10.

Visto de otra manera, si el DVD-R está $3,50, el BD-R debería costar $18,50 para ser igual de costoso.

Java Para Cálculos Numéricos

A veces se utiliza Java para hacer cálculos numéricos intensivos a pesar de su fama de ser lento. Muchas veces la rapidez de cálculo es un aspecto importante, pero casi nunca el único a la hora de elegir el lenguaje para implementar estos cálculos. Java tiene muchas cosas atractivas para este tipo de aplicaciones como el hecho de generar programas portables, de tener muy buen soporte para programación paralela y tener «garbage collector» entre otros. Pero, ¿qué tan lento o rápido es realmente para este tipo de cálculos?

Para tener una idea decidí comparar Java con C que es el lenguaje rápido por excelencia aunque en este campo muchos consideran a Fortran como en rey de la computación numérica.

Para la comparación tomé el algoritmo de Chudnovsky, que calcula el número PI con la cantidad de dígitos que se quiera, mientras se disponga de memoria suficiente para almacenarlos.

Para el cálculo en C usé la biblioteca GMP que permite hacer cálculos con precisión arbitraria y se autoproclama como la implementación más rápida del cálculo de PI.

En Java usé la biblioteca Apfloat que también ofrece la posibilidad de hacer cálculos con precisión arbitraria y que promete ser más rápida que la implementación de BigDecimal de Java para números grandes.

En esta era de computadoras con múltiples núcleos, opté por realizar el cálculo utilizando todo el paralelismo disponible y para ello utilicé dos implementaciones paralelizadas del algoritmo en cuestión. Apfloat ya trae una implementación paralela del algoritmo de Chudnovsky como ejemplo de uso y la implementación de C está basada en OpenMP.

  • Algoritmo de Chudnovsky en C
  • Algoritmo de Chudnovsky en Java
  • Opciones de compilación de C: GCC: 4.7.2  -fopenmp -Wall -O2 -lgmp -lm
  • Versión de GMP 5.0.2
  • Versión de Apfloat 1.7.1
  • Versión Java: OpenJDK 1.7.0u15
  • Hardware: AMD Phenom X4 9550 4 GB RAM
  • Sistema operativo: Ubuntu 12.10 64 bits
  • Memoria disponible para Java 2 GB

Parámetros usados para Apfloat

  • builderFactory=org.apfloat.internal.LongBuilderFactory
  • defaultRadix=10
  • cacheL1Size=131072
  • cacheL2Size=524288
  • cacheBurst=64
  • memoryTreshold=402653184
  • sharedMemoryTreshold=268435456
  • blockSize=1048576
  • numberOfProcessors=4
  • filePath=
  • fileInitialValue=0
  • fileSuffix=.ap
  • cleanupAtExit=true

Resultados

Dígitos Apfloat (segundos)
GMP (segundos) Apfloat/GMP
100.000 0,435 0,041 10,61
1.000.000 3,987 0,595 6,70
10.000.000 67,821 8,044 8,43
100.000.000 745,732 111,824 6,67

Los resultados muestran que en esta prueba la versión Java llega a ser 6 veces más lenta que GMP.

Como Apfloat utiliza almacenamiento en disco cuando los números son más grandes que un cierto tamaño (configurable), elegir la configuración adecuada lleva un poco de pruebas. La configuración por omisión no está del todo optimizada para computadoras actuales con varios gigas de RAM.

Código de la Prueba

El código C es el citado más arriba.

Java

package calculatepi;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apfloat.Apfloat;
import org.apfloat.ApfloatRuntimeException;
import org.apfloat.samples.Operation;
import org.apfloat.samples.Pi;
import org.apfloat.samples.PiParallel;

public class App {

    public static void main(String[] args) {
        final int max = Integer.parseInt(args[0]);
        Writer nullWritter = new Writer() {
            @Override
            public void write(char[] chars, int i, int i1) throws IOException {
            }

            @Override
            public void flush() throws IOException {
            }

            @Override
            public void close() throws IOException {
            }
        };

        try (
                PrintWriter out = new PrintWriter(nullWritter);
                PrintWriter err = new PrintWriter(new File("/home/user/apfloatPierr.txt"))) {
            Pi.setOut(out);
            Pi.setErr(err);
            final Operation warmUpOp = new PiParallel.ParallelChudnovskyPiCalculator(1000000, 10);

            final List<Operation> ops = new ArrayList<>();

            for (int i = 100000; i <= max; i = i * 10) {
                ops.add(new PiParallel.ParallelChudnovskyPiCalculator(i, 10));
            }

            for (int i = 0; i < 5; i++) {
                PiParallel.run(1000000, 10, warmUpOp);
            }
            int digits = 100000;
            for (Operation op : ops) {
                err.println("====================================");
                PiParallel.run(digits, 10, op);
                digits = digits * 10;
            }

        } catch (IOException | ApfloatRuntimeException ex) {
            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
}

Bash

#!/bin/sh

HEAP=2g
OUTFILE=apfloatPierr.txt
rm $OUTFILE

echo "Java..."

java -Xmx$HEAP -Xms$HEAP -jar /home/user/CalculatePi.jar 100000000
sleep 1

echo "C..."
echo "100000" >> $OUTFILE
./pi 100000 0 4 1> /dev/null 2>> $OUTFILE
echo "1000000" >> $OUTFILE
./pi 1000000 0 4 1> /dev/null 2>> $OUTFILE
echo "10000000" >> $OUTFILE
./pi 10000000 0 4 1> /dev/null 2>> $OUTFILE
echo "100000000" >> $OUTFILE
./pi 100000000 0 4 1> /dev/null 2>> $OUTFILE