Os aplicativos Java podem e devem usar a classe java.security.SecureRandom para produzir valores aleatórios criptograficamente fortes usando um gerador de números pseudo-aleatórios criptograficamente fortes ( CSPRNG ). As implementações JDK padrão da classe java.util.Random não são consideradas criptograficamente fortes.
Os sistemas operacionais do tipo Unix possuem /dev/random
um arquivo especial que serve para números pseudo-aleatórios acessando ruído ambiental coletado de drivers de dispositivo e outras fontes. No entanto, ele bloqueia se houver menos entropia disponível do que o solicitado ; /dev/urandom
normalmente nunca bloqueia, mesmo que a semente do gerador de números aleatórios pseudoaleatórios não tenha sido totalmente inicializada com entropia desde a inicialização. Ainda existe um terceiro arquivo especial, /dev/arandom
que bloqueia após a inicialização até que a semente tenha sido inicializada com segurança com entropia suficiente e nunca mais bloqueia novamente.
Por padrão, a JVM semeia a classe SecureRandom usando /dev/random
, portanto, seu código Java pode bloquear inesperadamente . A opção -Djava.security.egd=file:/dev/./urandom
na chamada da linha de comandos usada para iniciar o processo Java informa à JVM para usar /dev/urandom
.
O extra /./
parece fazer com que a JVM use o algoritmo SHA1PRNG, que usa o SHA-1 como base do PRNG (Pseudo Random Number Generator). É mais forte que o algoritmo NativePRNG usado quando /dev/urandom
é especificado.
Finalmente, existe um mito que /dev/urandom
é um gerador de números pseudo-aleatórios, um PRNG, enquanto /dev/random
é um gerador de números aleatórios “verdadeiro” . Isso simplesmente não é verdade, ambos /dev/random
e /dev/urandom
são alimentados pelo mesmo CSPRNG (gerador de números pseudoaleatórios criptograficamente seguros). Somente o comportamento quando o respectivo pool fica sem entropia, de acordo com alguma estimativa, difere: /dev/random
bloqueia, enquanto /dev/urandom
não.
E a entropia ficando baixa? Não importa.
Acontece que “parecer aleatório” é o requisito básico para muitos dos nossos blocos de construção criptográficos. E se você pegar a saída de um hash criptográfico, ele deve ser indistinguível de uma sequência aleatória para que as cifras o aceitem. Essa é a razão de usar o algoritmo SHA1PRNG, pois ele usa uma função hash e um contador, junto com uma semente.
Quando deve ser aplicado?
Sempre, eu diria.
Fontes:
https://gist.github.com/svrc/5a8accc57219b9548fe1
https://www.2uo.de/myths-about-urandom
EDIT 04/2020:
Um comentário menciona uma alteração no comportamento da classe SecureRandom no Java 8.
SHA1PRNG e NativePRNG foram corrigidos para respeitar adequadamente as propriedades de origem de semente SecureRandom no arquivo java.security. (A solução alternativa obscura usando file: /// dev / urandom e file: / dev /./ urandom não é mais necessária.)
Isso já havia sido apontado pelos testes mencionados na seção Fontes acima. O extra /./
é necessário para alterar o algoritmo usado pelo SecureRanom no Java 8 de NativePRNG para SHA1PRNG.
No entanto, tenho algumas notícias que gostaria de compartilhar. De acordo com o JEP-273 , desde o Java 9, a classe SecureRandom implementa os três mecanismos do Gerador de bits aleatórios determinísticos (DRBG) descritos no NIST 800-90Ar1 . Esses mecanismos implementam algoritmos modernos tão fortes quanto o SHA-512 e o AES-256.
O JDK tinha dois tipos de implementações do SecureRandom :
- Um deles depende da plataforma e é baseado em chamadas nativas ou dispositivos de SO, como a leitura
/dev/{u}random
no Unix ou o uso do CryptoAPI no Windows. As versões mais recentes do Linux e Windows já suportam DRBG, mas versões mais antigas e sistemas incorporados podem não .
- O outro tipo é uma implementação Java pura que usa uma implementação RNG baseada em SHA1 mais antiga, que não é tão forte quanto os algoritmos usados pelos mecanismos DRBG aprovados.
Enquanto isso, o Java 13 Security Developer's Guide ainda lê
No Linux e macOS, se o dispositivo de coleta de entropia em java.security estiver definido como file:/dev/urandom
ou file:/dev/random
, NativePRNG será preferido como SHA1PRNG. Caso contrário, é preferido o SHA1PRNG.
Para esclarecer como os novos mecanismos DRBG são executados em conjunto com os PRNGs anteriores, executei alguns testes no macOS (Darwin) com o AdoptOpenJDK (build 13.0.2 + 8). Aqui estão os resultados:
file: / dev / random
Ordem de preferência dos provedores:
SecureRandom.NativePRNG
SecureRandom.DRBG
SecureRandom.SHA1PRNG
file: / dev / urandom
Ordem de preferência dos provedores:
SecureRandom.NativePRNG
SecureRandom.DRBG
SecureRandom.SHA1PRNG
file: / dev /./ urandom
Ordem de preferência dos provedores:
SecureRandom.DRBG
SecureRandom.SHA1PRNG
SecureRandom.NativePRNG
Conclusão:
Eu recomendo o uso -Djava.security.egd=file:/dev/./urandom
para garantir o aproveitamento da implementação SecureRandom mais forte disponível, independentemente da plataforma usada, evitando o bloqueio inesperado do código.