Esta é a primeira página que aparece através do Google e as vulnerabilidades de segurança em todas as implementações me fazem estremecer, então eu estou postando isso para adicionar informações sobre criptografia para outras pessoas, pois faz 7 anos da publicação original. Eu tenho um mestrado em engenharia da computação e passei muito tempo estudando e aprendendo criptografia, então estou jogando meus dois centavos para tornar a internet um lugar mais seguro.
Além disso, observe que muita implementação pode ser segura para uma determinada situação, mas por que usá-las e, potencialmente, acidentalmente cometer um erro? Use as ferramentas mais fortes disponíveis, a menos que você tenha um motivo específico para não fazê-lo. No geral, eu recomendo usar uma biblioteca e ficar longe dos detalhes básicos, se puder.
ATUALIZAÇÃO 4/5/18: reescrevi algumas partes para torná-las mais simples de entender e alterei a biblioteca recomendada do Jasypt para a nova biblioteca do Google Tink , recomendo remover completamente o Jasypt de uma configuração existente.
Prefácio
A seguir, descreverei o básico da criptografia simétrica segura e apontarei os erros comuns que vejo online quando as pessoas implementam criptografia por conta própria com a biblioteca Java padrão. Se você quiser pular todos os detalhes, vá para a nova biblioteca do Google. Importe isso para o seu projeto e use o modo AES-GCM para todas as suas criptografias e você estará seguro.
Agora, se você quiser aprender os detalhes básicos sobre como criptografar em java, continue lendo :)
Cifras de bloco
Primeiro, você precisa escolher uma chave simétrica Block Cipher. Uma cifra de bloco é uma função / programa de computador usada para criar pseudo-aleatoriedade. A pseudo-aleatoriedade é uma aleatoriedade falsa que nenhum computador que não seja um computador quântico seria capaz de distinguir a diferença entre ele e a aleatoriedade real. A Cifra de Bloco é como o bloco de construção da criptografia e, quando usado com diferentes modos ou esquemas, podemos criar criptografias.
Agora, sobre os algoritmos de codificação de bloco disponíveis hoje, certifique-se de NUNCA , repito NUNCA use DES , diria mesmo NUNCA use 3DES . O único Block Cipher que até o lançamento da NSA de Snowden foi capaz de verificar estar realmente o mais próximo possível do pseudo-aleatório possível é o AES 256 . Também existe AES 128; a diferença é que o AES 256 funciona em blocos de 256 bits, enquanto o AES 128 funciona em 128 blocos. Em suma, o AES 128 é considerado seguro, embora algumas fraquezas tenham sido descobertas, mas 256 é o mais sólido possível.
Curiosidade O DES foi quebrado pela NSA quando foi inicialmente fundado e, na verdade, manteve um segredo por alguns anos. Embora algumas pessoas ainda afirmem que o 3DES é seguro, existem muitos trabalhos de pesquisa que encontraram e analisaram pontos fracos no 3DES .
Modos de criptografia
A criptografia é criada quando você pega uma cifra de bloco e usa um esquema específico para que a aleatoriedade seja combinada com uma chave para criar algo que seja reversível, desde que você conheça a chave. Isso é chamado de modo de criptografia.
Aqui está um exemplo de um modo de criptografia e o modo mais simples conhecido como BCE, para que você possa entender visualmente o que está acontecendo:
Os modos de criptografia que você verá mais comumente online são os seguintes:
CTR do BCE, CBC, GCM
Existem outros modos fora dos listados e os pesquisadores estão sempre trabalhando em novos modos para melhorar os problemas existentes.
Agora vamos às implementações e ao que é seguro. NUNCA use o BCE, isso é ruim para ocultar dados repetidos, como mostra o famoso pinguim Linux .
Ao implementar em Java, observe que, se você usar o seguinte código, o modo ECB será definido por padrão:
Cipher cipher = Cipher.getInstance("AES");
... PERIGO, ESTA É UMA VULNERABILIDADE! e, infelizmente, isso é visto em todo o StackOverflow e on-line em tutoriais e exemplos.
Nonces e IVs
Em resposta à questão encontrada no modo BCE, também foram criados nomes conhecidos como IVs. A idéia é que geremos uma nova variável aleatória e a anexemos a toda criptografia, para que, quando você criptografar duas mensagens iguais, elas saiam diferentes. A beleza por trás disso é que um IV ou nonce é de conhecimento público. Isso significa que um invasor pode ter acesso a isso, mas desde que não possua sua chave, não poderá fazer nada com esse conhecimento.
Problemas comuns que verei é que as pessoas definirão o IV como um valor estático, como no mesmo valor fixo em seu código. e aqui está a armadilha para os IVs no momento em que você repete um, você realmente compromete toda a segurança de sua criptografia.
Gerando um IV aleatório
SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");
byte[] iv = new byte[cipher.getBlockSize()];
randomSecureRandom.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
Nota: O SHA1 está quebrado, mas não consegui descobrir como implementar o SHA256 nesse caso de uso corretamente; portanto, se alguém quiser fazer uma rachadura e atualizá-lo, seria incrível! Além disso, os ataques SHA1 ainda não são convencionais, pois pode levar alguns anos em um grande cluster para quebrar. Confira os detalhes aqui.
Implementação de CTR
Não é necessário preenchimento para o modo CTR.
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
Implementação de CBC
Se você optar por implementar o Modo CBC, faça-o com PKCS7Padding da seguinte maneira:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
Vulnerabilidade de CBC e CTR e por que você deve usar o GCM
Embora alguns outros modos, como CBC e CTR, sejam seguros, eles enfrentam o problema em que um invasor pode inverter os dados criptografados, alterando seu valor quando descriptografado. Então, digamos que você criptografa uma mensagem bancária imaginária "Sell 100", sua mensagem criptografada se parece com "eu23ng", o invasor muda um pouco para "eu53ng" e, de repente, quando descriptografa sua mensagem, lê-se como "Sell 900".
Para evitar isso, a maioria da internet usa o GCM, e toda vez que você vê HTTPS, provavelmente está usando o GCM. O GCM assina a mensagem criptografada com um hash e verifica se a mensagem não foi alterada usando esta assinatura.
Eu evitaria implementar o GCM devido à sua complexidade. É melhor usar a nova biblioteca do Google, Tink, porque aqui novamente, se você repetir acidentalmente um IV, estará comprometendo a chave no caso do GCM, que é a falha definitiva de segurança. Novos pesquisadores estão trabalhando para os modos de criptografia resistente à repetição IV, onde, mesmo se você repetir o IV, a chave não está em perigo, mas isso ainda está por vir.
Agora, se você deseja implementar o GCM, aqui está um link para uma boa implementação do GCM . No entanto, não posso garantir a segurança ou se está devidamente implementada, mas reduz a base. Observe também que no GCM não há preenchimento.
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
Chaves vs senhas
Outra observação muito importante é que, quando se trata de criptografia, uma chave e uma senha não são as mesmas coisas. Uma chave na criptografia precisa ter uma certa quantidade de entropia e aleatoriedade para ser considerada segura. É por isso que você precisa se certificar de usar as bibliotecas criptográficas adequadas para gerar a chave para você.
Então você realmente tem duas implementações que você pode fazer aqui, a primeira é usar o código encontrado neste encadeamento StackOverflow para geração de chave aleatória . Esta solução usa um gerador de números aleatórios seguro para criar uma chave do zero que você pode usar.
A outra opção menos segura é usar a entrada do usuário, como uma senha. O problema, como discutimos, é que a senha não tem entropia suficiente, portanto, teríamos que usar o PBKDF2 , um algoritmo que pega a senha e a fortalece. Aqui está uma implementação StackOverflow que eu gostei . No entanto, a biblioteca do Google Tink tem tudo isso embutido e você deve tirar proveito disso.
Desenvolvedores Android
Um ponto importante a ser destacado aqui é saber que seu código Android é reversível e, na maioria dos casos, a maioria dos códigos Java também. Isso significa que se você armazenar a senha em texto sem formatação no seu código. Um hacker pode recuperá-lo facilmente. Normalmente, para esse tipo de criptografia, você deseja usar a criptografia assimétrica e assim por diante. Isso está fora do escopo deste post, por isso vou evitar mergulhar nele.
Uma leitura interessante de 2013 : salienta que 88% das implementações de criptografia no Android foram feitas de maneira inadequada.
Pensamentos finais
Mais uma vez, sugiro evitar a implementação da biblioteca java para criptografia diretamente e usar o Google Tink . Isso poupará a dor de cabeça, pois eles realmente fizeram um bom trabalho ao implementar todos os algoritmos corretamente. E mesmo assim, verifique as questões levantadas no github Tink, as vulnerabilidades aparecem aqui e ali.
Se você tiver alguma dúvida ou feedback, sinta-se à vontade para comentar! A segurança está sempre mudando e você precisa fazer o possível para acompanhá-la :)