¿Cómo resolver Java `SecureRandom` lento?

Si desea un número aleatorio criptográficamente fuerte en Java, use SecureRandom . Desafortunadamente, SecureRandom puede ser muy lento. Si usa /dev/random en Linux, puede bloquear esperando a que se acumule suficiente entropía. ¿Cómo se evita la penalización de rendimiento?

¿Alguien ha usado Uncommon Maths como solución a este problema?

¿Alguien puede confirmar que este problema de rendimiento se ha resuelto en JDK 6?

Si quieres datos aleatorios verdaderos, desafortunadamente tienes que esperarlos. Esto incluye la semilla para un SecureRandom PRNG. Uncommon Maths no puede recostackr datos aleatorios verdaderos más rápido que SecureRandom , aunque puede conectarse a Internet para descargar datos iniciales de un sitio web en particular. Supongo que es poco probable que sea más rápido que /dev/random cuando esté disponible.

Si quieres un PRNG, haz algo como esto:

 SecureRandom.getInstance("SHA1PRNG"); 

Las cadenas soportadas dependen del proveedor SecureRandom SPI, pero puede enumerarlas usando Security.getProviders() y Provider.getService() .

Sun es aficionado a SHA1PRNG, por lo que está ampliamente disponible. No es especialmente rápido a medida que avanzan los PRNG, pero los PRNG simplemente estarán crujiendo los números, no bloqueando la medición física de la entropía.

La excepción es que si no llama a setSeed() antes de obtener datos, entonces el PRNG se iniciará una vez la primera vez que llame a next() o nextBytes() . Normalmente lo hará utilizando una cantidad bastante pequeña de datos aleatorios verdaderos del sistema. Esta llamada puede bloquear, pero hará que su fuente de números aleatorios sea mucho más segura que cualquier variante de “hash la hora actual junto con el PID, agregue 27 y espere lo mejor”. Sin embargo, si todo lo que necesitas son números aleatorios para un juego, o si quieres que la transmisión sea repetible en el futuro utilizando la misma semilla para fines de prueba, una semilla insegura sigue siendo útil.

Debería poder seleccionar el / dev / urandom más rápido / pero menos seguro en Linux usando:

 -Djava.security.egd=file:/dev/urandom 

Sin embargo, esto no funciona con Java 5 y posterior ( Java Bug 6202721 ). El trabajo sugerido es usar:

 -Djava.security.egd=file:/dev/./urandom 

(tenga en cuenta el extra /./ )

En Linux, la implementación predeterminada para SecureRandom es NativePRNG (código fuente aquí ), que tiende a ser muy lento. En Windows, el valor predeterminado es SHA1PRNG , que, como otros señalaron, también se puede usar en Linux si se especifica explícitamente.

NativePRNG difiere de SHA1PRNG y Uncommons Maths ‘ AESCounterRNG en que continuamente recibe entropía del sistema operativo (leyendo de /dev/urandom ). Los otros PRNG no adquieren ninguna entropía adicional después de la siembra.

AESCounterRNG es aproximadamente 10 veces más rápido que SHA1PRNG , que IIRC es en sí mismo dos o tres veces más rápido que NativePRNG .

Si necesita un PRNG más rápido que adquiera entropía después de la inicialización, consulte si puede encontrar una implementación Java de Fortuna . El PRNG central de una implementación de Fortuna es idéntico al utilizado por AESCounterRNG, pero también hay un sistema sofisticado de agrupamiento de entropía y resiembra automática.

Tuve un problema similar con las llamadas al locking de SecureRandom durante unos 25 segundos a la vez en un servidor Debian sin cabeza. Instalé el daemon con haveged para asegurar que /dev/random se mantenga actualizado, en servidores sin cabeza necesitas algo como esto para generar la entropía requerida. Mis llamadas a SecureRandom ahora tal vez tomen milisegundos.

Muchas distribuciones de Linux (principalmente basadas en Debian) configuran OpenJDK para usar /dev/random para entropy.

/dev/random es por definición lento (e incluso puede bloquear).

Desde aquí tienes dos opciones sobre cómo desbloquearlo:

  1. Mejora la entropía, o
  2. Reducir los requisitos de aleatoriedad.

Opción 1, mejorar la entropía

Para obtener más entropía en /dev/random , pruebe con el daemon acrecido . Es un daemon que recostack continuamente la entropía HAVEGE y funciona también en un entorno virtual porque no requiere ningún hardware especial, solo la CPU y un reloj.

En Ubuntu / Debian:

 apt-get install haveged update-rc.d haveged defaults service haveged start 

En RHEL / CentOS:

 yum install haveged systemctl enable haveged systemctl start haveged 

Opción 2. Reducir los requisitos de aleatoriedad

Si por alguna razón la solución anterior no ayuda o no te importa la aleatoriedad criptográficamente fuerte, puedes cambiar a /dev/urandom lugar, lo que está garantizado para no bloquear.

Para hacerlo globalmente, edite el archivo jre/lib/security/java.security en su instalación Java predeterminada para usar /dev/urandom (debido a otro error , debe especificarse como /dev/./urandom ).

Me gusta esto:

 #securerandom.source=file:/dev/random securerandom.source=file:/dev/./urandom 

Entonces nunca tendrás que especificarlo en la línea de comando.


Nota: Si haces criptografía, necesitas buena entropía. Un ejemplo: el problema de PRNG de Android redujo la seguridad de las billeteras de Bitcoin.

Si quieres aleatoriedad verdaderamente “criptográficamente fuerte”, entonces necesitas una fuente de entropía fuerte. /dev/random es lento porque tiene que esperar a que los eventos del sistema recopilen entropía (lecturas de disco, paquetes de red, movimiento del mouse, pulsación de teclas, etc.).

Una solución más rápida es un generador de números aleatorios de hardware. Es posible que ya tenga uno integrado en su placa base; revise la documentación hw_random para obtener instrucciones sobre cómo averiguar si la tiene y cómo usarla. El paquete rng-tools incluye un daemon que alimentará la entropía generada por hardware en /dev/random .

Si un HRNG no está disponible en su sistema, y ​​está dispuesto a sacrificar la fuerza de entropía por el rendimiento, querrá sembrar un buen PRNG con datos de /dev/random , y dejar que el PRNG haga la mayor parte del trabajo. Hay varios PRNG aprobados por NIST enumerados en SP800-90 que son fáciles de implementar.

Use la fuente aleatoria segura como fuente de inicialización para un algoritmo recurrente; podría usar entonces un tornado Mersenne para el trabajo a granel en lugar de uno en UncommonMath, que ha existido por un tiempo y ha demostrado ser mejor que otros procesos.

http://en.wikipedia.org/wiki/Mersenne_twister

Asegúrese de actualizar de vez en cuando el azar seguro utilizado para la inicialización; por ejemplo, podría generar un seguro aleatorio generado por cliente, utilizando un generador seudoaleatorio mersenne twister por cliente, obteniendo un grado suficientemente alto de aleatorización

El problema al que hizo referencia sobre /dev/random no es con el algoritmo SecureRandom , sino con la fuente de aleatoriedad que utiliza. Los dos son ortogonales. Debes averiguar cuál de los dos te está ralentizando.

La página Uncommon Maths que usted ha vinculado menciona explícitamente que no están abordando el origen de la aleatoriedad.

Puede probar diferentes proveedores de JCE, como BouncyCastle, para ver si su implementación de SecureRandom es más rápida.

Una breve búsqueda también revela parches de Linux que reemplazan la implementación predeterminada con Fortuna. No sé mucho más sobre esto, pero puedes investigar.

También debo mencionar que si bien es muy peligroso usar un algoritmo SecureRandom mal implementado y / o una fuente de aleatoriedad, puede hacer que su propio proveedor de JCE implemente una aplicación personalizada de SecureRandomSpi . Tendrá que pasar por un proceso con Sun para firmar a su proveedor, pero en realidad es bastante sencillo; solo necesitan que les envíe por fax un formulario que indique que conoce las restricciones de exportación de los Estados Unidos a las bibliotecas de cifrado.

Mi experiencia ha sido solo con la inicialización lenta del PRNG, no con la generación de datos aleatorios después de eso. Pruebe una estrategia de inicialización más entusiasta. Dado que son caros de crear, trátelo como un singleton y reutilice la misma instancia. Si hay demasiada contención de subprocesos para una instancia, agréguelos o conviértalos en subprocesos locales.

No comprometa la generación de números aleatorios. Una debilidad allí compromete toda su seguridad.

No veo muchos generadores basados ​​en decaimiento atómico de COTS, pero existen varios planes para ellos, si realmente se necesitan muchos datos aleatorios. Un sitio que siempre tiene cosas interesantes para mirar, incluyendo HotBits, es el Fourmilab de John Walker.

Enfrenté el mismo problema . Después de buscar en Google con los términos de búsqueda correctos, me encontré con este buen artículo en Digital Ocean .

haveged es una solución potencial sin comprometer la seguridad.

Simplemente estoy citando la parte relevante del artículo aquí.

Basado en el principio HAVEGE, y previamente basado en su biblioteca asociada, haveged permite generar aleatoriedad según las variaciones en el tiempo de ejecución del código en un procesador. Dado que es casi imposible que una parte del código tome el mismo tiempo para ejecutarse, incluso en el mismo entorno en el mismo hardware, el tiempo de ejecución de uno o varios progtwigs debería ser adecuado para generar una fuente aleatoria. La implementación de hasged genera la fuente aleatoria de su sistema (generalmente / dev / random) usando diferencias en el contador de marca de tiempo de su procesador (TSC) después de ejecutar un bucle repetidamente

Cómo instalar haveged

Sigue los pasos en este artículo. https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged

Lo he publicado aquí

Parece que deberías ser más claro acerca de tus requisitos de RNG. El requisito de RNG criptográfico más fuerte (según tengo entendido) sería que, incluso si conoce el algoritmo utilizado para generarlos, y conoce todos los números aleatorios generados previamente, no podría obtener ninguna información útil sobre ninguno de los números aleatorios generados en el futuro, sin gastar una cantidad poco práctica de poder de cómputo.

Si no necesita esta garantía completa de aleatoriedad, entonces probablemente haya compensaciones de rendimiento apropiadas. Tiendo a estar de acuerdo con la respuesta de Dan Dyer sobre AESCounterRNG de Uncommons-Maths, o Fortuna (uno de sus autores es Bruce Schneier, un experto en criptografía). Nunca he usado ninguno pero las ideas parecen acreditadas a primera vista.

Creo que si pudieras generar una semilla aleatoria inicial periódicamente (por ejemplo, una vez por día, hora o lo que sea), podrías usar un cifrado de flujo rápido para generar números aleatorios a partir de fragmentos sucesivos de la secuencia (si el cifrado de flujo usa XOR entonces solo pasar una secuencia de nulos o tomar los bits XOR directamente). El proyecto eStream de ECRYPT tiene mucha información buena, incluyendo puntos de referencia de rendimiento. Esto no mantendría la entropía entre los puntos en el tiempo que usted reponga, por lo que si alguien supiera uno de los números aleatorios y el algoritmo que utilizó, técnicamente podría ser posible, con una gran cantidad de poder de cómputo, descifrar el cifrado de flujo y adivinar su estado interno para poder predecir números aleatorios futuros. Pero tendría que decidir si ese riesgo y sus consecuencias son suficientes para justificar el costo de mantener la entropía.

Editar: aquí hay algunas notas del curso criptográfico sobre RNG que encontré en la red que parecen muy relevantes para este tema.

Hay una herramienta (al menos en Ubuntu) que alimentará la aleatoriedad artificial en su sistema. El comando es simplemente:

 rngd -r /dev/urandom 

y es posible que necesite un sudo en el frente. Si no tiene el paquete de herramientas rng, tendrá que instalarlo. Intenté esto, ¡y definitivamente me ayudó!

Fuente: mate vs mundo

Usando Java 8, encontré que en Linux llamar a SecureRandom.getInstanceStrong() me daría el algoritmo NativePRNGBlocking . Esto a menudo bloqueará durante muchos segundos para generar unos pocos bytes de sal.

Pasé a pedir explícitamente NativePRNGNonBlocking y, como esperaba del nombre, ya no está bloqueado. No tengo idea de cuáles son las implicaciones de seguridad de esto. Presumiblemente, la versión sin locking no puede garantizar la cantidad de entropía que se utiliza.

Actualización : Ok, encontré esta excelente explicación .

En pocas palabras, para evitar el locking, use el new SecureRandom() . Esto usa /dev/urandom , que no bloquea y es básicamente tan seguro como /dev/random . Desde la publicación: “La única vez que desea llamar a / dev / random es cuando la máquina arranca por primera vez y la entropía aún no se ha acumulado”.

SecureRandom.getInstanceStrong() te ofrece el RNG más potente, pero solo es seguro para usar en situaciones donde un montón de locking no te afectará.

No he atacado este problema yo mismo, pero generaría un hilo en el inicio del progtwig que de inmediato intenta generar una semilla, luego muere. El método que usted llame para randoms se unirá a ese hilo si está activo, por lo que la primera llamada solo se bloquea si se produce muy temprano en la ejecución del progtwig.

Otra cosa a mirar es la propiedad securerandom.source en el archivo lib / security / java.security

Puede haber un beneficio de rendimiento al usar / dev / urandom en lugar de / dev / random. Recuerde que si la calidad de los números aleatorios es importante, no haga concesiones que rompan la seguridad.

Si su hardware lo admite, intente usar Java RdRand Utility disponible en: http://code.google.com/p/lizalab-rdrand-util/

Está basado en la instrucción RDRAND de Intel y es aproximadamente 10 veces más rápido que SecureRandom y no tiene problemas de ancho de banda para la implementación de grandes volúmenes.

Divulgación completa, soy el autor de la utilidad.

Puede probar el proyecto Apache commons Math, que tiene algunas implementaciones de algoritmos conocidos:

https://commons.apache.org/proper/commons-math/userguide/random.html

Sin embargo, ten cuidado con el rendimiento. El constructor predeterminado de RandomDataGenerator crea una instancia dedicada de Well19937c , que es una operación muy costosa.

Según la documentación, esta clase no es segura para subprocesos, pero si puede garantizar que solo un subproceso tendrá acceso a esta clase, puede inicializar solo una instancia por subproceso.

Planteamiento del problema

Esta biblioteca msprandom demuestra una técnica de generación de números aleatorios para propósitos criptográficos sin generadores de hardware. El cifrado y la firma requieren números aleatorios de buena calidad. Generar números aleatorios (o secuencias de bytes aleatorios) sin generadores de hardware no es una tarea trivial. Especialmente este problema es real para dispositivos pequeños donde las fonts de datos aleatorios están ausentes o limitadas. La solución es tener semilla aleatoria verdadera guardada en un archivo protegido (bóveda) y cifrado que puede producir secuencias encriptadas generadas pseudoaleatorias (PRNG) basadas en semilla aleatoria con buenas características aleatorias.

Muchas bibliotecas criptográficas (por ejemplo, BouncyCastle) usan la clase SecureRandom para el cifrado y la firma para obtener números aleatorios. SecureRandom depende de la implementación del sistema operativo. Otra de las palabras, la realización de motor aleatorio está fuera de su aplicación, que no puede controlar. Para evitar el uso de números aleatorios pobres, DEBE sembrar el generador SecureRandom con buenos datos aleatorios cada vez que llame a funciones criptográficas que requieran datos aleatorios. O puede extender la clase SecureRandom con su realización que produce un número aleatorio cuya calidad puede controlar.

Idea

Necesitamos usar datos aleatorios verdaderos almacenados en una bóveda de datos segura.

Algunos pasos de cómo msprandom dentro de su aplicación:

  1. Genere en su computadora o computadora portátil una semilla aleatoria verdadera y póngala en una bóveda usando esta biblioteca.
  2. Coloque una bóveda (archivo) con una semilla aleatoria en su dispositivo, computadora o servidor donde necesita encriptar y firmar datos.
  3. Cargue la bóveda una vez al inicio del progtwig cuando necesite encriptar o firmar datos.
  4. Llame a la función gen-rand de la biblioteca msprandom para obtener bytes aleatorios tantas veces como lo necesite.

La bóveda con semilla aleatoria está encriptada y asegurada con HMAC. La semilla aleatoria en una bóveda se actualiza cada vez que carga la bóveda de manera impredecible, por lo que HMAC también está cambiando. Cambiar una bóveda se hace intencionalmente en contra de la situación si el atacante puede enriquecer alguna copia de tu bóveda en el pasado.

Verdadero generador de datos aleatorios

Para generar una verdadera semilla aleatoria se usa una entrada humana en msprandom . Aquí está el algoritmo de recostackción de datos aleatorios:

  1. Ejecutamos hilo separado donde el contador atómico incrementa cada tic desde 0..255 con una velocidad muy alta.
  2. Espere a que el humano presione la tecla sin búfer y obtenga un código de exploración del botón presionado.
  3. Tome el valor actual de nanosegundos desde el inicio de Epoch y tome el mod 256 para convertir su valor a un byte aleatorio.
  4. Xor valores entre sí: scan-code-byte ^ current-counter-value ^ nanoseconds para producir byte aleatorio.
  5. Agregue un byte aleatorio al vector de salida. Suponemos que solo 3 bits tienen aleatoriedad verdadera en este byte aleatorio. Por lo tanto, para obtener 32 bytes aleatorios verdaderos, necesitamos presionar 32 ~ 3 botones desde la entrada del usuario.
  6. Repita los pasos 2 a 5 hasta que obtengamos la cantidad requerida de bytes aleatorios. Si recogimos la cantidad requerida de datos aleatorios, hagamos el paso final -> hash vector de salida con función hash criptográficamente fuerte para garantizar que la probabilidad 1 y 0 bits en el vector de salida será 0.5. Tenga en cuenta que la función hash utilizada aquí solo para mezclar bits aleatorios y no influye en la calidad de los datos aleatorios. Entonces hash (datos aleatorios) = datos aleatorios. Usando este algoritmo, el msprandom recoge un verdadero 512 bits aleatorios como una semilla que se guardará en una bóveda.

¿Por qué 512 bits aleatorios es suficiente?

Bueno, cada PRNG necesita una verdadera semilla aleatoria. Si un atacante conoce una semilla, entonces puede predecir la generación de claves, etc. 256 bits de semilla aleatoria inicial es lo suficientemente lejos como para mantener secretos de grado millitary. Hice 512 para asegurarme de que nadie puede usar la fuerza bruta ni adivinar la semilla aleatoria inicial. Entonces, puedes usar libremente msprandom para sembrar tus generadores PRNG o SecureRandom.

    Intereting Posts