Como colocar a tag de autenticação GCM no final de um fluxo de criptografia exige buffer interno durante a descriptografi
Em Java, o provedor "AES / GCM" padrão SunJCE irá - durante o processo de descriptografia - armazenar internamente em buffer 1) bytes criptografados usados como entrada ou 2)ytes descriptografados produzidos como resulta. O código do aplicativo que está descriptografando notará queCipher.update(byte[])
retorna uma matriz de bytes vazia eCipher.update(ByteBuffer, ByteBuffer)
retorna o comprimento escrito 0. Então, quando o processo for concluído,Cipher.doFinal()
retornarátodo bytes decodificados.
Primeira pergunta é: Quais bytes estão sendo armazenados em buffer, número 1 ou número 2 acim
Presumo que o buffer ocorra apenas durante a descriptografia e não a criptografia, porque, em primeiro lugar, os problemas que surgem desse buffer (descritos em breve) não ocorrem no meu cliente Java fazendo a criptografia dos arquivos lidos no disco, sempre ocorrem no lado do servidor , recebendo esses arquivos e fazendo a descriptografia. Em segundo lugar, diz-se queAqu. A julgar apenas pela minha própria experiência, não tenho certeza porque meu cliente usa umCipherOutputStream
. O cliente não usa explicitamente métodos na instância Cipher. Portanto, não posso deduzir se o buffer interno é usado ou não, porque não consigo ver o que os métodos update e final retorna
Meus problemas reais surgem quando os arquivos criptografados que eu transmito do cliente para o servidor se tornam grandes. Em geral, quero dizer mais de 100 MB.
O que acontece então é que Cipher.update () lança umOutOfMemoryError
. Obviamente, devido ao buffer interno crescendo e crescendo.
Além disso, apesar de buffer interno e nenhum resultado de bytes recebidos de Cipher.update (), Cipher.getOutputSize (int) reporte continuamente um tamanho crescente do buffer de destino. Portanto, meu código de aplicativo é forçado a alocar um @ cada vez maiByteBuffer
que é alimentado em Cipher.update (ByteBuffer, ByteBuffer). Se eu tentar trapacear e passar um buffer de bytes com uma capacidade menor, o método de atualização lançará umShortBufferException
# 1. Saber que eu crio enormes buffers de bytes sem uso é bastante desmoralizant
Dado que o buffer interno é a raiz de todos os males, a solução óbvia para mim aplicar aqui é dividir os arquivos em pedaços, digamos 1 MB cada - nunca tenho problemas para enviar arquivos pequenos, apenas arquivos grandes. Mas luto muito para entender por que o buffer interno acontece em primeiro luga
O link anteriormenteEntão respond diz que a tag de autenticação GCM: s é "adicionada no final do texto cifrado", mas que "não precisa ser colocada no final" e essa prática é o que "atrapalha a natureza online da descriptografia do GCM"
Por que colocar a tag no final apenas atrapalha o trabalho do servidor de descriptografar?
Aqui está como vai meu raciocínio. Para calcular uma marca de autenticação, ou MAC, se desejar, o cliente usa algum tipo de função hash. Pelo visto,MessageDigest.update()
não usa um buffer interno cada vez maio
Então, no lado receptor, o servidor não pode fazer a mesma coisa? Para iniciantes, ele pode descriptografar os bytes, embora não autenticados, alimentá-los em sua função de atualização do algoritmo de hash e, quando a tag chegar, terminar o resumo e verificar o MAC que o cliente enviou.
Eu não sou um cara de criptografia, por favor, fale comigo como se eu fosse burro e louco, mas amando o suficiente para cuidar de alguns =) Agradeço de todo o coração pelo tempo necessário para ler essa pergunta e talvez até lançar alguma luz!
UPDATE # 1Eu não uso AD (Dados Associados).
UPDATE # 2oftware @Wrote que demonstra a criptografia AES / GCM usando Java, bem como oSecure Remote Protocol (SRP) e transferências de arquivos binários no Java EE. O cliente front-end é escrito em JavaFX e pode ser usado para alterar dinamicamente a configuração de criptografia ou enviar arquivos usando blocos. No final de uma transferência de arquivo, são apresentadas algumas estatísticas sobre o tempo usado para transferir o arquivo e o tempo para descriptografia do servidor. O repositório também possui um documento com algumas das minhas próprias pesquisas relacionadas ao GCM e Java.
Aproveitar:https: //github.com/MartinanderssonDotcom/secure-login-file-transfer
# 1 É interessante notar que, se meu servidor que faz a descriptografia não manipular a cifra, ele usará umCipherInputStream
, OutOfMemoryError não é lançado. Em vez disso, o cliente consegue transferir todos os bytes pela conexão, mas em algum lugar durante a descriptografia, o encadeamento de solicitação é interrompido indefinidamente e posso ver que um encadeamento Java (pode ser o mesmo encadeamento) utiliza totalmente um núcleo da CPU, enquanto deixa o arquivo ligado disco inacessível e com um tamanho de arquivo relatado igual a 0. Depois de muito tempo, oCloseable
source está fechado e minha cláusula catch consegue capturar uma IOException causada por: "javax.crypto.AEADBadTagException: entrada muito curta - precisa de tag".
O que torna essa situação estranha é que a transmissão de arquivos menores funciona perfeitamente com o mesmo código - portanto, obviamente, a tag pode ser verificada corretamente. O problema deve ter a mesma causa raiz que ao usar a cifra explicitamente, ou seja, um buffer interno sempre crescente. Não consigo rastrear no servidor quantos bytes foram lidos / decifrados com sucesso, porque assim que a leitura do fluxo de entrada da cifra começa, a reordenação do compilador ou outras otimizações de JIT fazem com que todas as minhas instruções de registro evaporem no ar. Aparentemente, eles não são executados.
Observe queeste projeto do GitHub e seus associadosblog post diz que CipherInputStream está quebrado. Mas os testes fornecidos por este projeto não falham para mim ao usar o Java 8u25 e o provedor SunJCE. E, como já foi dito, tudo funciona para mim se eu usar apenas arquivos pequeno