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 # 1

Eu não uso AD (Dados Associados).

UPDATE # 2

oftware @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

questionAnswers(3)

yourAnswerToTheQuestion