Pregunta

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

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

¿Alguien puede confirmar que este problema de rendimiento se haya solucionado en JDK 6?

¿Fue útil?

Solución

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

Si quieres un PRNG, haz algo como esto:

SecureRandom.getInstance("SHA1PRNG");

Las cadenas admitidas dependen del proveedor de SPI SecureRandom , pero puede enumerarlas utilizando Security.getProviders () y Provider.getService () .

Sun le gusta SHA1PRNG, por lo que está ampliamente disponible. No es especialmente rápido como lo hacen los PRNG, pero los PRNG serán números simples, no bloquearán la medición física de la entropía.

La excepción es que si no llama a setSeed () antes de obtener datos, PRNG se activará una vez la primera vez que llame a next () o nextBytes () . Por lo general, lo hará utilizando una cantidad bastante pequeña de datos aleatorios reales 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 de la hora actual junto con el PID, agregue 27, y espere lo mejor " ;. Sin embargo, si todo lo que necesita son números aleatorios para un juego, o si desea que la transmisión sea repetible en el futuro utilizando la misma semilla para propósitos de prueba, una semilla insegura sigue siendo útil.

Otros consejos

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

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

Sin embargo, esto no funciona con Java 5 y versiones posteriores ( Bug Java 6202721 ). La solución alternativa sugerida es utilizar:

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

(note el /./ extra)

En Linux, la implementación predeterminada de SecureRandom es NativePRNG (código fuente here ), que suele ser muy lento. En Windows, el valor predeterminado es SHA1PRNG , que, como otros han señalado, también puede usar en Linux si lo especifica explícitamente.

NativePRNG se diferencia de SHA1PRNG y Uncommons Maths ' AESCounterRNG en que recibe continuamente entropía del sistema operativo (leyendo desde / 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 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, vea 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 agrupación de entropía y resiembra automática.

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

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

Desde aquí tienes dos opciones para desbloquearlo:

  1. Mejorar 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 el haveged daemon. Es un demonio que recopila continuamente la entropía de HAVEGE y también funciona en un entorno virtualizado 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 algún motivo, la solución anterior no ayuda o si no te importa la aleatoriedad criptográficamente sólida, puedes cambiar a / dev / urandom , lo que garantiza que no se bloqueará.

Para hacerlo globalmente, edite el archivo jre / lib / security / java.security en su instalación Java predeterminada para usar / dev / urandom (debido a otra < a href = "https://bugs.openjdk.java.net/browse/JDK-6202721" rel = "noreferrer"> 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 comandos.


Nota: si realiza la criptografía, necesita una buena entropía. Caso en cuestión: problema de PRNG en Android redujo la seguridad de las billeteras de Bitcoin.

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

Si quieres realmente "criptográficamente fuerte" Aleatoriedad, 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, pulsaciones 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 incorporado en su placa base; Consulte la hw_random documentación para obtener instrucciones sobre cómo averiguar si lo tiene, y cómo usarlo. El paquete rng-tools incluye un demonio que alimentará la entropía generada por hardware en / dev / random .

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

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

rngd -r /dev/urandom

y es posible que necesites un sudo en la parte delantera. Si no tiene el paquete rng-tools, deberá instalarlo. Intenté esto, y definitivamente me ayudó!

Fuente: matt vs world

Me encontré con el mismo problema . Después de buscar en Google con los términos de búsqueda adecuados, me encontré con este bonito artículo en DigitalOcean .

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 anteriormente basado en sus asociados   biblioteca, haveged permite generar aleatoriedad basada en variaciones en   Código de tiempo de ejecución en un procesador. Ya que es casi imposible para   una pieza de código para tomar el mismo tiempo exacto de ejecución, incluso en el   mismo entorno en el mismo hardware, la sincronización de la ejecución de un solo   o múltiples programas deben ser adecuados para sembrar una fuente aleatoria. los   la implementación hasged siembra la fuente aleatoria de su sistema (generalmente   / dev / random) usando las diferencias en el contador de marca de tiempo de su procesador   (TSC) después de ejecutar un bucle repetidamente

Cómo instalar hasged

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í

Utilice el aleatorio seguro como fuente de inicialización para un algoritmo recurrente; podría usar un Twister de Mersenne para el trabajo a granel en lugar del de UncommonMath, que ha existido por un tiempo y ha demostrado ser mejor que otros prng

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

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

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

Cambié a solicitar explícitamente NativePRNGNonBlocking en su lugar, y como se 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 bloqueo no puede garantizar la cantidad de entropía que se está utilizando.

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

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

SecureRandom.getInstanceStrong () te da el RNG más fuerte absoluto, pero es seguro usarlo en situaciones donde un montón de bloqueos no te afectará.

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 vinculaste explícitamente menciona que no están abordando el origen de la aleatoriedad.

Puedes 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 el predeterminado Implementación 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 rodar su propio proveedor de JCE con una implementación personalizada de SecureRandomSpi . Deberá pasar por un proceso con Sun para obtener la firma de 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 EE. UU. a las bibliotecas criptográficas.

Yo no he golpeado contra este problema por mí mismo, pero al principio del programa generé un hilo que inmediatamente intenta generar una semilla y luego muere. El método al que llamas randoms se unirá a ese hilo si está vivo, por lo que la primera llamada solo se bloquea si ocurre muy temprano en la ejecución del programa.

Mi experiencia ha sido solo con la lenta inicialización del PRNG, no con la generación de datos aleatorios después de eso. Intente una estrategia de inicialización más ansiosa. Ya 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úpelas o conviértalas en subprocesos.

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

No veo muchos generadores basados ??en COTS decay & # 8211; pero hay varios planes para ellos, si realmente necesitas muchos datos aleatorios. Un sitio que siempre tiene cosas interesantes que ver, como HotBits, es Fourmilab de John Walker.

Parece que debería ser más claro con respecto a sus requisitos de RNG. El requisito de RNG criptográfico más fuerte (como lo entiendo) sería que incluso si conoce el algoritmo utilizado para generarlos, y conoce todos los números aleatorios generados anteriormente, no podría obtener ninguna información útil sobre ninguno de los números aleatorios generados en el Futuro, sin gastar una cantidad impráctica de poder de cómputo.

Si no necesita esta garantía completa de aleatoriedad, es probable que haya una compensación adecuada de rendimiento. Tendría a estar de acuerdo con Respuesta de Dan Dyer sobre AESCounterRNG de Uncommons-Maths, o Fortuna (uno de sus autores es Bruce Schneier, experto en criptografía). Nunca lo he usado, pero las ideas parecen ser de buena reputación a primera vista.

pensaría que si pudiera generar una inicialización aleatoria inicial periódicamente (por ejemplo, una vez por día u hora o lo que sea), podría usar un cifrado de flujo rápido para generar números aleatorios a partir de fragmentos sucesivos de flujo (si el cifrado de flujo usa XOR, simplemente pase un flujo de nulos o tome los bits de XOR directamente). El proyecto eStream de ECRYPT tiene mucha información útil, incluidos puntos de referencia de rendimiento. Esto no mantendría la entropía entre los puntos en el tiempo en que la repones, por lo que si alguien supiera uno de los números aleatorios y el algoritmo que utilizaste, técnicamente podría ser posible, con mucha potencia de cómputo, romper el cifrado de flujo y Adivina su estado interno para poder predecir futuros números aleatorios. 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 Encontré en la red que parece muy relevante para este tema.

Si su hardware lo admite, intente utilizando la utilidad Java RdRand de la que soy el autor .

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


Tenga en cuenta que esta implementación solo funciona en aquellas CPU que proporcionan la instrucción (es decir, cuando se establece el indicador del procesador rdrand ). Debe crear una instancia explícita a través del constructor RdRandRandom () ; no se ha implementado ningún Provider específico.

Algo más a considerar 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 un compromiso que rompa la seguridad.

Puedes probar Apache commons Math project, que tiene algunas implementaciones de algoritmos bien conocidos:

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

Sin embargo, tenga 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 accederá a esta clase, puede inicializar solo una instancia por subproceso.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top