Se você quiser números aleatórios criptograficamente fortes em Java, use SecureRandom
. Infelizmente, SecureRandom
pode ser muito lento. Se ele usa /dev/random
no Linux, pode bloquear a espera de entropia suficiente para se acumular. Como você evita a penalidade de desempenho?
Alguém já usou Uncommon Maths como uma solução para este problema?
Alguém pode confirmar que esse problema de desempenho foi resolvido no JDK 6?
Respostas:
Se você quiser dados aleatórios verdadeiros, infelizmente precisará esperar por eles. Isso inclui a semente de um
SecureRandom
PRNG. O Uncommon Maths não pode reunir dados aleatórios verdadeiros com mais rapidez do queSecureRandom
, embora possa se conectar à Internet para baixar dados de sementes de um site específico. Meu palpite é que é improvável que seja mais rápido do/dev/random
que o local disponível.Se você deseja um PRNG, faça algo assim:
Quais strings são suportadas dependem do
SecureRandom
provedor SPI, mas você pode enumerá-las usandoSecurity.getProviders()
eProvider.getService()
.A Sun gosta de SHA1PRNG, por isso está amplamente disponível. Não é tão rápido quanto os PRNGs, mas os PRNGs serão apenas trituradores de números, não bloqueando a medição física da entropia.
A exceção é que, se você não ligar
setSeed()
antes de obter dados, o PRNG se propagará uma vez na primeira vez que você ligarnext()
ounextBytes()
. Geralmente, isso é feito usando uma quantidade bastante pequena de dados aleatórios verdadeiros do sistema. Essa chamada pode bloquear, mas tornará sua fonte de números aleatórios muito mais segura do que qualquer variante de "hash da hora atual junto com o PID, adicione 27 e espere o melhor". Porém, se tudo o que você precisa é de números aleatórios para um jogo ou se você deseja que o stream possa ser repetido no futuro usando a mesma semente para fins de teste, uma semente insegura ainda será útil.fonte
Você deve poder selecionar o / dev / urandom mais rápido, mas um pouco menos seguro, no Linux usando:
No entanto, isso não funciona com o Java 5 e posterior ( Java Bug 6202721 ). A solução alternativa sugerida é usar:
(observe o extra
/./
)fonte
/dev/urandom
, a Sun a trata como uma sequência mágica e a usa de/dev/random
qualquer maneira, então você precisa fingir. Quando umfile:
URL não é umfile:
URL? Sempre que a Sun decide que não é :-(file:/dev/urandom
definida no-Djava.security.egd
ou nosecurerandom.source
arquivo java.security,/dev/random/
ainda é lida sempre queSecureRandom.getSeed()
(ousetSeed()
é chamada). A solução alternativa comfile:/dev/./urandom
resultados em não ler/dev/random
nada (confirmada com strace) #/dev/urandom
não é menos seguro do que/dev/random
quando implementado com um CSPRNG moderna: en.wikipedia.org/wiki//dev/random#FreeBSD/dev/urandom/
é o que acontece se você usá-lo para gerar segredos em um novo hardware pronto para uso, o que pode estar em um estado bastante previsível./dev/urandom/
não bloqueará a entropia, mesmo que esse seja o caso em que você deveria. A situação é ainda pior se o segredo for persistente, como se a primeira coisa que seu dispositivo fizesse na primeira inicialização fosse gerar um par de chaves público-privado. Fora dessas situações assustadoras, um bem/dev/urandom
é melhor do que usar osSecureRandom
algoritmos comuns de qualquer maneira.No Linux, a implementação padrão para
SecureRandom
éNativePRNG
(código fonte aqui ), que tende a ser muito lenta. No Windows, o padrão éSHA1PRNG
que, como outros apontaram, você também pode usar no Linux se especificá-lo explicitamente.NativePRNG
difere deSHA1PRNG
e Uncommons AESCounterRNG do Maths por receber continuamente entropia do sistema operacional (lendo em/dev/urandom
). Os outros PRNGs não adquirem nenhuma entropia adicional após a semeadura.O AESCounterRNG é cerca de 10x mais rápido que o IIRC
SHA1PRNG
, ele próprio, duas ou três vezes mais rápido queNativePRNG
.Se você precisar de um PRNG mais rápido que adquira entropia após a inicialização, veja se consegue encontrar uma implementação Java do Fortuna . O PRNG principal de uma implementação Fortuna é idêntico ao usado pelo AESCounterRNG, mas também existe um sistema sofisticado de pool de entropia e nova propagação automática.
fonte
Muitas distribuições Linux (principalmente baseadas no Debian) configuram o OpenJDK para uso
/dev/random
em entropia./dev/random
por definição, é lento (e pode até bloquear).A partir daqui, você tem duas opções para desbloqueá-lo:
Opção 1, melhorar a entropia
Para obter mais entropia
/dev/random
, tente o daemon protegido . É um daemon que coleta continuamente a entropia HAVEGE e funciona também em um ambiente virtualizado porque não requer nenhum hardware especial, apenas a própria CPU e um relógio.No Ubuntu / Debian:
No RHEL / CentOS:
Opção 2. Reduzir os requisitos de aleatoriedade
Se, por algum motivo, a solução acima não ajudar ou você não se importar com a aleatoriedade criptograficamente forte, você pode alternar para o
/dev/urandom
que é garantido para não bloquear.Para fazer isso globalmente, edite o arquivo
jre/lib/security/java.security
em sua instalação Java padrão para usar/dev/urandom
(devido a outro bug, ele precisa ser especificado como/dev/./urandom
).Como isso:
Então você nunca precisará especificá-lo na linha de comando.
Nota: Se você faz criptografia, precisa de uma boa entropia. Caso em questão - o problema do PRNG no Android reduziu a segurança das carteiras Bitcoin.
fonte
/dev/random
é por definição lenta (e pode até bloquear)" está errada; depende inteiramente da configuração do sistema. Máquinas mais recentes podem ter, por exemplo, um RNG rápido na CPU que pode ser usada, e máquinas BSD geralmente têm a mesma implementação para/dev/random
e/devl/urandom
. Ainda assim, você provavelmente não deve confiar em/dev/random
ser rápido, necessariamente. Nas VMs, convém instalar o conjunto de ferramentas do cliente na VM do cliente para poder usar o RNG do sistema operacional host.Eu tive um problema semelhante com as chamadas para
SecureRandom
bloqueio por cerca de 25 segundos por vez em um servidor Debian decapitado. Eu instalei ohaveged
daemon para garantir a/dev/random
manutenção, em servidores sem cabeça, você precisa de algo assim para gerar a entropia necessária. Minhas ligações paraSecureRandom
agora talvez levem milissegundos.fonte
Se você deseja uma aleatoriedade verdadeiramente "criptograficamente forte", precisará de uma fonte de entropia forte.
/dev/random
é lento porque precisa aguardar os eventos do sistema para coletar entropia (leituras de disco, pacotes de rede, movimento do mouse, pressionamentos de teclas etc.).Uma solução mais rápida é um gerador de números aleatórios de hardware. Você já pode ter um embutido na sua placa-mãe; consulte a documentação do hw_random para obter instruções sobre como descobrir se você a possui e como usá-la. O pacote rng-tools inclui um daemon que alimentará a entropia gerada por hardware
/dev/random
.Se um HRNG não estiver disponível no seu sistema e você estiver disposto a sacrificar a força da entropia para obter desempenho, deseje gerar um bom PRNG com dados de
/dev/random
e deixar que o PRNG faça a maior parte do trabalho. Existem vários PRNGs aprovados pelo NIST listados no SP800-90 que são fáceis de implementar.fonte
Usando o Java 8, descobri que na chamada do Linux
SecureRandom.getInstanceStrong()
me daria oNativePRNGBlocking
algoritmo. Isso geralmente bloqueia por muitos segundos para gerar alguns bytes de sal.Em
NativePRNGNonBlocking
vez disso, mudei para pedir explicitamente e, como esperado, o nome não está mais bloqueado. Não tenho ideia de quais são as implicações de segurança disso. Presumivelmente, a versão sem bloqueio não pode garantir a quantidade de entropia usada.Atualização : Ok, achei esta excelente explicação .
Em poucas palavras, para evitar o bloqueio, use
new SecureRandom()
. Isso usa/dev/urandom
, o que não bloqueia e é basicamente tão seguro quanto/dev/random
. Da postagem: "A única vez em que você deseja chamar / dev / random é quando a máquina é inicializada pela primeira vez e a entropia ainda não foi acumulada".SecureRandom.getInstanceStrong()
fornece o RNG mais forte, mas é seguro apenas para uso em situações em que muitos bloqueios não afetam você.fonte
getInstanceStrong()
chaves de longo prazo, como as dos certificados TLS. E mesmo assim, prefiro usarnew SecureRandom()
ou um gerador de pares de chaves compatível com FIPS ou gerador de números aleatórios. Então, sim, isso fornece uma resposta, se/dev/urandom
não bloquear: no final, ainda depende da entropia do sistema; mas é um conselho muito bom em geral . Se houver/dev/urandom
bloqueios, talvez seja necessário corrigir a origem do problema e não o aplicativo Java.Existe uma ferramenta (pelo menos no Ubuntu) que alimenta aleatoriedade artificial em seu sistema. O comando é simplesmente:
e você pode precisar de um sudo na frente. Se você não possui o pacote rng-tools, será necessário instalá-lo. Eu tentei isso, e definitivamente me ajudou!
Fonte: mundo vs matt
fonte
sudo rngd -r /dev/urandom
comsudo apt install rng-tools
em xenialEu enfrentei o mesmo problema . Depois de pesquisar no Google com os termos de pesquisa certos, me deparei com este belo artigo no DigitalOcean .
haveged é uma solução potencial sem comprometer a segurança.
Estou apenas citando a parte relevante do artigo aqui.
Como instalar haveged
Siga as etapas deste artigo. https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged
Eu publiquei aqui
fonte
O problema que você referenciou
/dev/random
não está noSecureRandom
algoritmo, mas na fonte de aleatoriedade que ele usa. Os dois são ortogonais. Você deve descobrir qual dos dois está atrasando você.A página incomum de matemática que você vinculou menciona explicitamente que eles não estão abordando a fonte da aleatoriedade.
Você pode experimentar diferentes provedores JCE, como o BouncyCastle, para verificar se a implementação deles
SecureRandom
é mais rápida.Uma breve pesquisa também revela patches do Linux que substituem a implementação padrão pelo Fortuna. Não sei muito mais sobre isso, mas você pode investigar.
Também devo mencionar que, embora seja muito perigoso usar um
SecureRandom
algoritmo mal implementado e / ou uma fonte aleatória, você pode rolar seu próprio provedor JCE com uma implementação customizada deSecureRandomSpi
. Você precisará passar por um processo com a Sun para assinar seu provedor, mas na verdade é bastante direto; eles só precisam que você envie por fax um formulário informando que você conhece as restrições de exportação dos EUA em bibliotecas de criptografia.fonte
Use o aleatório seguro como fonte de inicialização para um algoritmo recorrente; você poderia usar um twister de Mersenne para o trabalho em massa, em vez daquele no UncommonMath, que já existe há algum tempo e provou ser melhor do que outros
http://en.wikipedia.org/wiki/Mersenne_twister
Atualize de vez em quando o aleatório seguro usado para a inicialização; por exemplo, você pode ter um aleatório seguro gerado por cliente, usando um gerador pseudo-aleatório mersenne twister por cliente, obtendo um grau de randomização alto o suficiente
fonte
Random
, mas não paraSecureRandom
.De acordo com a documentação , os diferentes algoritmos usados pelo SecureRandom são, em ordem de preferência:
Desde que você perguntou sobre o Linux, estou ignorando a implementação do Windows, e também o SunPKCS11, que só está realmente disponível no Solaris, a menos que você o tenha instalado - e você não perguntaria isso.
De acordo com a mesma documentação, o que esses algoritmos usam são
SHA1PRNG
A propagação inicial é atualmente feita por meio de uma combinação de atributos do sistema e o dispositivo de coleta de entropia java.security.
NativePRNG
nextBytes()
usa/dev/urandom
generateSeed()
usos/dev/random
NativePRNGBlocking
nextBytes()
egenerateSeed()
uso/dev/random
NativePRNGNonBlocking
nextBytes()
egenerateSeed()
uso/dev/urandom
Isso significa que, se você usar
SecureRandom random = new SecureRandom()
, ele desce essa lista até encontrar uma que funcione, que normalmente será NativePRNG. E isso significa que ele se propaga a partir de/dev/random
(ou usa isso se você gerar explicitamente uma semente), e então usa/dev/urandom
para obter os próximos bytes, ints, double, booleanos, o que você tem.Como
/dev/random
está bloqueando (ele bloqueia até ter entropia suficiente no pool de entropia), isso pode impedir o desempenho.Uma solução para isso é usar algo como Haveged para gerar entropia suficiente, outra solução está sendo usada
/dev/urandom
. Embora você possa definir isso para toda a jvm, uma solução melhor é fazer isso para essa instância específica doSecureRandom
usandoSecureRandom random = SecureRandom.getInstance("NativePRNGNonBlocking")
. Observe que esse método pode gerar uma NoSuchAlgorithmException se NativePRNGNonBlocking, portanto, esteja preparado para retornar ao padrão.Observe também que em outros sistemas * nix,
/dev/urandom
pode se comportar de maneira diferente .É
/dev/urandom
aleatório o suficiente?A sabedoria convencional diz que apenas
/dev/random
é aleatória o suficiente. No entanto, algumas vozes diferem. Em "A maneira correta de usar o SecureRandom" e "Mitos sobre / dev / urandom" , argumenta-se que isso/dev/urandom/
é igualmente bom.Os usuários na pilha de Segurança da informação concordam com isso . Basicamente, se você precisar perguntar, tudo
/dev/urandom
bem para o seu propósito.fonte
Eu mesmo não enfrentei esse problema, mas geraria um encadeamento no início do programa que imediatamente tenta gerar uma semente e depois morre. O método que você chama de randoms se unirá a esse encadeamento se estiver ativo; portanto, a primeira chamada será bloqueada apenas se ocorrer muito cedo na execução do programa.
fonte
Minha experiência foi apenas com a lenta inicialização do PRNG, não com a geração de dados aleatórios depois disso. Tente uma estratégia de inicialização mais ávida. Como sua criação é cara, trate-a como um singleton e reutilize a mesma instância. Se houver muita contenção de encadeamentos em uma instância, agrupe-os ou torne-os encadeados locais.
Não comprometa a geração aleatória de números. Uma fraqueza compromete toda a sua segurança.
Não vejo muitos geradores baseados em decaimento atômico do COTS, mas existem vários planos para eles, se você realmente precisa de muitos dados aleatórios. Um site que sempre tem coisas interessantes para se olhar, incluindo o HotBits, é o Fourmilab de John Walker.
fonte
SecureRandom
mudou algumas vezes nos últimos 10 anos em sua coleta de entropia.SecureRandom
ainda seja um problema, mas a baixa entropia em um sistema sempre será um problema. Usar um singleton criará um código fortemente acoplado, que é um antipadrão de design. Portanto, deve ser usado com extremo cuidado; de preferência, você teria que reverter todas as referências no código para corrigir o problema.Parece que você deve ser mais claro sobre seus requisitos de RNG. O requisito de RNG criptográfico mais forte (como eu o entendo) seria que, mesmo que você conheça o algoritmo usado para gerá-los e conheça todos os números aleatórios gerados anteriormente, não poderá obter informações úteis sobre nenhum dos números aleatórios gerados no futuro, sem gastar uma quantidade impraticável de poder de computação.
Se você não precisar dessa garantia completa de aleatoriedade, provavelmente haverá trocas de desempenho apropriadas. Eu tenderia a concordar com a resposta de Dan Dyer sobre o AESCounterRNG da Uncommons-Maths ou Fortuna (um de seus autores é Bruce Schneier, especialista em criptografia). Eu também nunca usei, mas as idéias parecem respeitáveis à primeira vista.
Eu pensaria que se você pudesse gerar uma semente aleatória inicial periodicamente (por exemplo, uma vez por dia ou hora ou qualquer outra coisa), poderia usar uma cifra de fluxo rápido para gerar números aleatórios a partir de pedaços sucessivos do fluxo (se a cifra de fluxo usar XOR, apenas passe um fluxo de nulos ou pegue os bits XOR diretamente). EStream do ECRYPTO projeto possui muitas informações boas, incluindo benchmarks de desempenho. Isso não manteria a entropia entre os pontos no tempo em que você o reabasteceria; portanto, se alguém conhecesse um dos números aleatórios e o algoritmo usado, tecnicamente seria possível, com muita capacidade de computação, interromper a cifra do fluxo e adivinhe seu estado interno para poder prever números aleatórios futuros. Mas você teria que decidir se esse risco e suas conseqüências são suficientes para justificar o custo de manutenção da entropia.
Edit: aqui estão algumas notas de curso criptográficas sobre RNG que encontrei na rede que parecem muito relevantes para este tópico.
fonte
Se o seu hardware suportar, tente usar o Java RdRand Utility, do qual sou o autor.
Ele é baseado nas
RDRAND
instruções da Intel e é cerca de 10 vezes mais rápido do queSecureRandom
e sem problemas de largura de banda para a implementação de grandes volumes.Observe que essa implementação funciona apenas nas CPUs que fornecem a instrução (ou seja, quando o
rdrand
sinalizador do processador está definido). Você precisa instanciar explicitamente isso através doRdRandRandom()
construtor; nenhum específicoProvider
foi implementado.fonte
RDRAND
é uma boa fonte, mas não é confiável. Definitivamente precisa ser uma contribuição de muitos para um colecionador (sem ofensa a David Johnston).Outra coisa a se observar é a propriedade securerandom.source no arquivo lib / security / java.security
Pode haver um benefício no desempenho ao usar / dev / urandom em vez de / dev / random. Lembre-se de que se a qualidade dos números aleatórios for importante, não comprometa a segurança.
fonte