Si quieres un número aleatorio criptográficamente fuerte en Java, lo usas SecureRandom
. Lamentablemente, SecureRandom
puede ser muy lento. Si se usa /dev/random
en Linux, puede bloquear la espera de que se acumule suficiente entropía. ¿Cómo se evita la penalización de rendimiento?
¿Alguien ha usado Matemáticas poco comunes como solución a este problema?
¿Alguien puede confirmar que este problema de rendimiento se ha resuelto en JDK 6?
Respuestas:
Si desea datos aleatorios verdaderos, desafortunadamente debe esperarlos. Esto incluye la semilla para un
SecureRandom
PRNG. Uncommon Maths no puede recopilar datos aleatorios verdaderos más rápido queSecureRandom
, aunque puede conectarse a Internet para descargar datos semilla de un sitio web en particular. Supongo que es poco probable que sea más rápido de lo/dev/random
que está disponible.Si quieres un PRNG, haz algo como esto:
Las cadenas que se admiten dependen del
SecureRandom
proveedor de SPI, pero puede enumerarlas conSecurity.getProviders()
yProvider.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 solo serán números crujientes, no bloqueando la medición física de la entropía.
La excepción es que si no llama
setSeed()
antes de obtener datos, el PRNG se iniciará una vez la primera vez que llamenext()
onextBytes()
. Por lo general, hará esto 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 necesita son números aleatorios para un juego, o si desea que la transmisión sea repetible en el futuro usando la misma semilla para fines de prueba, una semilla insegura sigue siendo útil.fuente
Debería poder seleccionar el más rápido pero un poco menos seguro / dev / urandom en Linux usando:
Sin embargo, esto no funciona con Java 5 y versiones posteriores ( Java Bug 6202721 ). La solución sugerida es usar:
(tenga en cuenta el extra
/./
)fuente
/dev/urandom
, Sun trata esto como una cadena mágica y lo utiliza de/dev/random
todos modos, por lo que debe simularlo. ¿Cuándo unafile:
URL no es unafile:
URL? Cada vez que Sun decide que no es así :-(file:/dev/urandom
set in-Djava.security.egd
o insecurerandom.source
en el archivo java.security,/dev/random/
todavía se lee cada vezSecureRandom.getSeed()
(osetSeed()
se llama). La solución alternativa confile:/dev/./urandom
resultados en no leer/dev/random
nada (confirmado con strace)/dev/urandom
no es menos seguro que/dev/random
cuando se implementa con un CSPRNG moderno: en.wikipedia.org/wiki//dev/random#FreeBSD/dev/urandom/
es qué sucede si lo usas para generar secretos en el nuevo hardware fuera de la caja, que podría estar en un estado bastante predecible./dev/urandom/
no bloqueará la entropía a pesar de que ese es un caso en el que deberías. La situación es aún peor si el secreto es persistente, como si lo primero que hace su dispositivo en el primer arranque es generar un par de claves pública-privada. Fuera de esas situaciones de miedo, un bien/dev/urandom
es mejor que usar losSecureRandom
algoritmos comunes de todos modos.En Linux, la implementación predeterminada para
SecureRandom
esNativePRNG
(código fuente aquí ), que tiende a ser muy lenta. En Windows, el valor predeterminado esSHA1PRNG
, que como otros señalaron, también puede usar en Linux si lo especifica explícitamente.NativePRNG
difiere de AESCounterRNG deSHA1PRNG
Uncommons Maths en que recibe continuamente entropía del sistema operativo (al leer de ). Los otros PRNG no adquieren ninguna entropía adicional después de la siembra./dev/urandom
AESCounterRNG es aproximadamente 10 veces más rápido que
SHA1PRNG
, IIRC es en sí mismo dos o tres veces más rápido queNativePRNG
.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 sofisticado sistema de agrupación de entropía y reposición automática.
fuente
Muchas distribuciones de Linux (en su mayoría basadas en Debian) configuran OpenJDK para usar
/dev/random
en entropía./dev/random
es, por definición, lento (e incluso puede bloquear).Desde aquí tienes dos opciones sobre cómo desbloquearlo:
Opción 1, mejorar la entropía
Para obtener más entropía
/dev/random
, prueba el demonio adosado . Es un demonio que recolecta continuamente entropía HAVEGE, y funciona también en un entorno virtualizado porque no requiere ningún hardware especial, solo la CPU y un reloj.En Ubuntu / Debian:
En RHEL / CentOS:
Opción 2. Reducir los requisitos de aleatoriedad
Si por alguna razón la solución anterior no ayuda o no le importa la aleatoriedad criptográficamente fuerte, puede 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 otro error , debe especificarse como/dev/./urandom
).Me gusta esto:
Entonces nunca tendrá que especificarlo en la línea de comando.
Nota: Si haces criptografía, necesitas una buena entropía. Caso en cuestión: el problema de Android PRNG redujo la seguridad de las billeteras de Bitcoin.
fuente
/dev/random
por definición es lento (e incluso puede bloquear)" está mal; depende completamente de la configuración del sistema. Las máquinas más nuevas pueden tener, por ejemplo, un RNG rápido en la CPU que puede usarse, y las máquinas BSD generalmente tienen la misma implementación para/dev/random
y/devl/urandom
. Aún así, probablemente no deberías confiar en/dev/random
ser rápido, necesariamente. En las máquinas virtuales, es posible que desee instalar el conjunto de herramientas del cliente en la máquina virtual del cliente para poder utilizar el RNG del sistema operativo host.Tuve un problema similar con las llamadas al
SecureRandom
bloqueo durante aproximadamente 25 segundos a la vez en un servidor Debian sin cabeza. Instalé elhaveged
daemon para asegurarme de que/dev/random
esté lleno, en servidores sin cabeza necesitas algo como esto para generar la entropía requerida. Mis llamadas aSecureRandom
ahora tal vez tarden milisegundos.fuente
Si quieres una 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, 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 una integrada en su placa base; Consulte la documentación de hw_random para obtener instrucciones sobre cómo averiguar si la tiene y cómo usarla. El paquete rng-tools incluye un demonio que alimentará la entropía generada por hardware
/dev/random
.Si un HRNG no está disponible en su sistema, y está dispuesto a sacrificar la fuerza de la 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 el NIST enumerados en SP800-90 que son fáciles de implementar.fuente
Usando Java 8, descubrí que en Linux las llamadas
SecureRandom.getInstanceStrong()
me darían elNativePRNGBlocking
algoritmo. Esto a menudo bloqueará durante muchos segundos para generar unos pocos bytes de sal.Cambié a pedirlo explícitamente
NativePRNGNonBlocking
, y como se esperaba del nombre, ya no se bloqueó. 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 utiliza.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
. De la publicación: "La única vez que desea llamar / dev / random es cuando la máquina se inicia por primera vez y la entropía aún no se ha acumulado".SecureRandom.getInstanceStrong()
te proporciona el RNG más fuerte, pero solo es seguro de usar en situaciones en las que un montón de bloqueos no te afectará.fuente
getInstanceStrong()
claves a largo plazo, como las de los certificados TLS. E incluso entonces preferiría usarnew SecureRandom()
un generador de pares de claves o un generador de números aleatorios que cumplan con FIPS. Entonces, sí, esto proporciona una respuesta, si/dev/urandom
no se bloquea: al final, aún depende de la entropía del sistema; pero es muy buen consejo en general . Si/dev/urandom
bloquea, es posible que deba solucionar el origen del problema en lugar de su aplicación Java.Hay una herramienta (al menos en Ubuntu) que alimentará la aleatoriedad artificial en su sistema. El comando es simplemente:
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
fuente
sudo rngd -r /dev/urandom
consudo apt install rng-tools
xenialMe enfrenté al mismo problema . Después de buscar en Google con los términos de búsqueda correctos, me encontré con este bonito artículo sobre DigitalOcean .
haveged es una solución potencial sin comprometer la seguridad.
Simplemente estoy citando la parte relevante del artículo aquí.
Cómo instalar haveged
Sigue los pasos de este artículo. https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged
Lo he publicado aquí.
fuente
El problema al que hizo referencia
/dev/random
no es con elSecureRandom
algoritmo, sino con la fuente de aleatoriedad que utiliza. Los dos son ortogonales. Debes descubrir cuál de los dos te está frenando.La página de Matemáticas poco común que ha vinculado explícitamente menciona que no abordan la fuente de aleatoriedad.
Puede probar diferentes proveedores de JCE, como BouncyCastle, para ver si su implementación
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 utilizar un
SecureRandom
algoritmo mal implementado y / o una fuente de aleatoriedad, puede implementar su propio proveedor JCE con una implementación personalizada deSecureRandomSpi
. 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 de cifrado.fuente
Utilice el aleatorio seguro como fuente de inicialización para un algoritmo recurrente; podría usar un tornado Mersenne para el trabajo a granel en lugar del UncommonMath, que ha existido durante un tiempo y ha demostrado ser mejor que otro 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 mersenne twister por cliente, obteniendo un grado suficientemente alto de aleatorización
fuente
Random
, pero no paraSecureRandom
.Según la documentación , los diferentes algoritmos utilizados por SecureRandom son, en orden de preferencia:
Dado que usted preguntó acerca de Linux, estoy ignorando la implementación de Windows, y también SunPKCS11, que solo está realmente disponible en Solaris, a menos que lo haya instalado usted mismo, y entonces no lo estaría preguntando.
Según esa misma documentación, lo que usan estos algoritmos son
SHA1PRNG
La inicialización inicial se realiza actualmente mediante una combinación de atributos del sistema y el dispositivo de recolección de entropía java.security.
NativePRNG
nextBytes()
utiliza/dev/urandom
generateSeed()
usos/dev/random
NativePRNGBbloqueo
nextBytes()
ygenerateSeed()
uso/dev/random
NativePRNGNonBlocking
nextBytes()
ygenerateSeed()
uso/dev/urandom
Eso significa que si lo usa
SecureRandom random = new SecureRandom()
, baja esa lista hasta que encuentre uno que funcione, que generalmente será NativePRNG. Y eso significa que se siembra desde/dev/random
(o usa eso si explícitamente genera una semilla), luego lo usa/dev/urandom
para obtener los siguientes bytes, ints, double, booleans, what-have-yous.Dado que
/dev/random
está bloqueando (bloquea hasta que tiene suficiente entropía en el grupo de entropía), eso puede impedir el rendimiento.Una solución para eso es usar algo como forjado para generar suficiente entropía, otra solución está usando en su
/dev/urandom
lugar. Si bien puede configurarlo para todo el jvm, una mejor solución es hacerlo para esta instancia específica deSecureRandom
, mediante el usoSecureRandom random = SecureRandom.getInstance("NativePRNGNonBlocking")
. Tenga en cuenta que ese método puede generar una NoSuchAlgorithmException si NativePRNGNonBlocking, así que prepárese para recurrir al valor predeterminado.También tenga en cuenta que en otros sistemas * nix,
/dev/urandom
puede comportarse de manera diferente .¿Es lo
/dev/urandom
suficientemente aleatorio?La sabiduría convencional dice que solo
/dev/random
es lo suficientemente aleatorio. Sin embargo, algunas voces difieren. En "La forma correcta de usar SecureRandom" y "Mitos sobre / dev / urandom" , se argumenta que/dev/urandom/
es igual de bueno.Los usuarios de la pila de seguridad de la información están de acuerdo con eso . Básicamente, si tiene que preguntar,
/dev/urandom
está bien para su propósito.fuente
No me he enfrentado a este problema, pero engendré un hilo al inicio del programa que inmediatamente intenta generar una semilla, luego muere. El método al que llamas randoms se unirá a ese hilo si está activo, por lo que la primera llamada solo se bloquea si ocurre muy temprano en la ejecución del programa.
fuente
Mi experiencia ha sido solo con una lenta inicialización del PRNG, no con la generación de datos aleatorios después de eso. Pruebe una estrategia de inicialización más entusiasta. Como son caros de crear, trátelo como un singleton y reutilice la misma instancia. Si hay demasiada contención de hilos para una instancia, agrúpelos o conviértalos en hilos locales.
No comprometa la generación de números aleatorios. Una debilidad allí compromete toda su seguridad.
No veo muchos generadores basados en la desintegración atómica COTS, pero hay varios planes para ellos, si realmente necesita una gran cantidad de datos aleatorios. Un sitio que siempre tiene cosas interesantes para ver, incluidos HotBits, es el Fourmilab de John Walker.
fuente
SecureRandom
ha cambiado un par de veces en los últimos 10 años en su reunión de entropía.SecureRandom
siga siendo un problema, pero una baja entropía en un sistema siempre será un problema. El uso de un singleton creará un código fuertemente acoplado, que es un antipatrón de diseño. Por lo tanto, debe usarse con extremo cuidado; preferiblemente tendría que revertir todas las referencias en el código para solucionar el problema.Parece que debería ser más claro acerca de sus 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 potencia informática.
Si no necesita esta garantía completa de aleatoriedad, probablemente existan compensaciones de rendimiento apropiadas. Tendería 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 tampoco, pero las ideas parecen ser respetables a primera vista.
Me gustaría pensar que si se pudiera generar una semilla aleatoria inicial periódicamente (por ejemplo, una vez al día o una hora o lo que sea), se puede utilizar un cifrado de flujo rápido para generar números aleatorios a partir de trozos sucesivos de la corriente (si el cifrado de flujo utiliza XOR a continuación, sólo pase una secuencia de nulos o tome los bits XOR directamente). EStream de ECRYPTEl proyecto tiene mucha buena información, incluyendo puntos de referencia de rendimiento. Esto no mantendría la entropía entre los puntos en el tiempo en que la repone, por lo que si alguien conociera uno de los números aleatorios y el algoritmo que utilizó, técnicamente podría ser posible, con mucha potencia informática, romper el cifrado de flujo y adivina 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.
fuente
Si su hardware lo admite, intente utilizar Java RdRand Utility, de la cual soy el autor.
Se basa en las
RDRAND
instrucciones de Intel y es aproximadamente 10 veces más rápido queSecureRandom
y sin problemas de ancho de banda para la implementación de gran volumen.Tenga en cuenta que esta implementación solo funciona en aquellas CPU que proporcionan la instrucción (es decir, cuando se establece el
rdrand
indicador del procesador). Necesita crear una instancia explícita a través delRdRandRandom()
constructor; NoProvider
se ha implementado ningún específico .fuente
RDRAND
es una buena fuente, pero es un poco poco confiable. Definitivamente debe ser una entrada de muchos en un coleccionista (sin ofender a David Johnston).Otra cosa a tener en cuenta 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.
fuente