Cómo es que colocar la etiqueta de autenticación GCM al final de una secuencia de cifrado requiere almacenamiento en búfer interno durante el descifrado?

En Java, el proveedor "predeterminado" AES / GCM SunJCE, durante el proceso de descifrado, almacenará el búfer interno 1) bytes cifrados utilizados como entrada o 2) bytes descifrados producidos como resultado. El código de la aplicación que realiza el descifrado notará queCipher.update(byte[]) devuelve una matriz de bytes vacía yCipher.update(ByteBuffer, ByteBuffer) regresar longitud escrita 0. Luego, cuando el proceso se complete,Cipher.doFinal() devolverátodo bytes decodificados.

La primera pregunta es: ¿qué bytes están siendo almacenados, el número 1 o el número 2 arriba?

Supongo que el almacenamiento en búfer se produce solo durante el descifrado y no en el cifrado porque, en primer lugar, los problemas que surgen de este almacenamiento en búfer (descrito brevemente) no se producen en mi cliente Java al cifrar los archivos leídos del disco, siempre ocurren en el lado del servidor , recibiendo esos archivos y haciendo el descifrado. En segundo lugar, se dice queaqu. A juzgar solo por mi propia experiencia, no puedo estar seguro porque mi cliente usa unCipherOutputStream. El cliente no utiliza métodos explícitamente en la instancia de Cipher. Por lo tanto, no puedo deducir si se usa o no el almacenamiento en búfer interno porque no puedo ver qué devuelven el método de actualización y final.

Mis problemas reales surgen cuando los archivos cifrados que transmito del cliente al servidor se hacen grandes. Por grande me refiero a más de 100 MB.

o que sucede entonces es que Cipher.update () arroja unOutOfMemoryError. Obviamente debido al buffer interno que crece y crece.

Además, a pesar del almacenamiento en búfer interno y no se recibieron bytes de resultados de Cipher.update (), Cipher.getOutputSize (int) informa continuamente una longitud de búfer objetivo cada vez mayor. Por lo tanto, mi código de aplicación se ve obligado a asignar un @ cada vez mayByteBuffer que se alimenta a Cipher.update (ByteBuffer, ByteBuffer). Si intento hacer trampa y pasar un búfer de bytes con una capacidad menor, entonces el método de actualización arroja unShortBufferException # 1. Saber que creo enormes buffers de bytes sin uso es bastante desmoralizante.

Dado que el almacenamiento en búfer interno es la raíz de todo mal, entonces la solución obvia para mí aplicar aquí es dividir los archivos en trozos, por ejemplo de 1 MB cada uno; nunca tengo problemas para enviar archivos pequeños, solo archivos grandes. Pero, me cuesta mucho entender por qué el almacenamiento intermedio interno ocurre en primer lugar.

El @ previamente vinculaPues contest dice que la etiqueta de autenticación de GCM: "se agrega al final del texto cifrado", pero que "no tiene que colocarse al final" y esta práctica es lo que "ensucia la naturaleza en línea del descifrado de GCM".

¿Por qué poner la etiqueta al final solo arruina el trabajo de descifrado del servidor?

Así es como va mi razonamiento. Para calcular una etiqueta de autenticación, o MAC, si lo desea, el cliente utiliza algún tipo de función hash. Al parecer,MessageDigest.update() no utiliza un búfer interno cada vez mayor.

Entonces, en el extremo receptor, ¿no puede el servidor hacer lo mismo? Para empezar, puede descifrar los bytes, aunque no estén autenticados, alimentarlos en su función de actualización del algoritmo hash y cuando llegue la etiqueta, finalizar el resumen y verificar el MAC que envió el cliente.

No soy un tipo criptográfico, así que por favor háblame como si fuera tonto y loco, pero lo suficientemente amoroso como para preocuparme por algunos =) ¡Te agradezco de todo corazón por el tiempo dedicado a leer esta pregunta y tal vez incluso arrojar algo de luz! @

UPDATE # 1

No uso AD (datos asociados).

UPDATE # 2

oftware @Wrote que demuestra el cifrado AES / GCM usando Java, así como el Protocolo remoto seguro (SRP) y transferencias de archivos binarios en Java EE. El cliente front-end está escrito en JavaFX y se puede usar para cambiar dinámicamente la configuración de cifrado o enviar archivos mediante fragmentos. Al final de una transferencia de archivos, se presentan algunas estadísticas sobre el tiempo utilizado para transferir el archivo y el tiempo para que el servidor descifre. El repositorio también tiene un documento con algunas de mis propias investigaciones relacionadas con GCM y Java.

Disfrute:https: //github.com/MartinanderssonDotcom/secure-login-file-transfer

# 1

s interesante notar que si mi servidor que hace el descifrado no maneja el cifrado él mismo, en su lugar usa unCipherInputStream, entonces no se arroja OutOfMemoryError. En cambio, el cliente logra transferir todos los bytes a través del cable, pero en algún lugar durante el descifrado, el hilo de solicitud se cuelga indefinidamente y puedo ver que un hilo de Java (podría ser el mismo hilo) utiliza completamente un núcleo de CPU, todo mientras deja el archivo encendido disco inaccesible y con un tamaño de archivo reportado de 0. Luego, después de una cantidad de tiempo tremendamente larga, laCloseable source está cerrado y mi cláusula catch logra capturar una IOException causada por: "javax.crypto.AEADBadTagException: entrada demasiado corta - necesita etiqueta".

Lo que hace que esta situación sea extraña es que la transmisión de archivos más pequeños funciona perfectamente con el mismo código, por lo que obviamente la etiqueta se puede verificar correctamente. El problema debe tener la misma causa raíz que cuando se usa el cifrado explícitamente, es decir, un búfer interno cada vez mayor. No puedo rastrear en el servidor cuántos bytes se leyeron / descifraron con éxito porque tan pronto como comienza la lectura de la secuencia de entrada de cifrado, el reordenamiento del compilador u otras optimizaciones JIT hacen que todas mis declaraciones de registro se evaporen en el aire. [Aparentemente] no se ejecutan en absoluto.

Tenga en cuenta queeste proyecto GitHub y sus @ asociadentrada en el blo dice CipherInputStream está roto. Pero las pruebas proporcionadas por este proyecto no me fallan cuando uso Java 8u25 y el proveedor SunJCE. Y como ya se ha dicho, todo funciona para mí si solo uso archivos pequeños.

Respuestas a la pregunta(3)

Su respuesta a la pregunta