¿Cómo actualizar parte de los datos cifrados con datos recién cifrados?

Necesito cifrar un archivo de audio mientras se está generando. Estoy cifrando el encabezado con datos ficticios (porque no conozco el tamaño real de los datos de audio) al iniciar y cifrar los datos de audio sobre la marcha. Mi plan es actualizar el encabezado al final con el tamaño de datos real del archivo de audio.

Pero, cuando intenté sobrescribir los datos de encabezado cifrados con datos de encabezado recién cifrados del mismo tamaño utilizando la misma clave y IV y luego tratar de descifrar, obtengo datos no deseados generados.

¿Por qué sucede esto aunque estoy usando la misma clave y IV? En el siguiente código traté de simular lo que estoy haciendo. Archivo cifrado de tamaño de 64 bytes generado y archivo descifrado de tamaño de 50 bytes generado.

Sin actualización: abcdabcdab0123456789012345678901234567890123456789

Con actualización de cabecera: ABCDABCDAB ÷ ‹þ @ óMCKLZƒÖ ^ Ô234567890123456789

Salida esperada: ABCDABCDAB0123456789012345678901234567890123456789

¿Es este el enfoque correcto para lograr una actualización parcial de los datos ya cifrados?

protected void Encrypt() { byte[] numBytes = {'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', '0','1','2','3','4','5','6','7','8','9', '0','1','2','3','4','5','6','7','8','9'}; byte[] smallCase = {'a','b','c','d','a','b','c','d','a','b','c','d','a','b','c','d'}; byte[] capitalCase = {'A','B','C','D','A','B','C','D','A','B','C','D','A','B','C','D'}; try { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1And8BIT"); KeySpec spec = new PBEKeySpec("junglebook".toCharArray(), "Salt".getBytes(), 65536, 256); SecretKey tmp = null; tmp = factory.generateSecret(spec); SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); /* Encryption cipher initialization. */ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secret); AlgorithmParameters params = cipher.getParameters(); byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV(); Log.d("Encryption" + "iv data :", iv.toString()); /*Open two Cipher ouput streams to the same encrypted file*/ FileOutputStream os = new FileOutputStream(sdCard.getAbsolutePath() + "/Notes/sample.encrypted"); CipherOutputStream cos = new CipherOutputStream(os,cipher); FileOutputStream os1 = new FileOutputStream(sdCard.getAbsolutePath() + "/Notes/sample.encrypted"); CipherOutputStream cos1 = new CipherOutputStream(os1,cipher); int offset = 0; Log.d("Encryption", "Writing cipher text to output file"); //Write 16 bytes header data with smallCase array cos.write(smallCase, offset, 16); // write 40 bytes actual data cos.write(numBytes, offset, 40); FileOutputStream ivStream = new FileOutputStream(sdCard.getAbsolutePath() + "/Notes/iv.dat"); if (ivStream != null) { Log.d("Encryption", "Writing iv data to output file"); ivStream.write(iv); } cos.close(); // Overwrite header data with capitalCase array data cos1.write(capitalCase, offset, 16); cos1.close(); ivStream.close(); }catch (Exception e) { e.printStackTrace(); } } protected void Decrypt() { byte[] dBytes = new byte[200]; try { Log.d("Decryption", "Reading iv data "); File f1 = new File(sdCard.getAbsolutePath()+"/Notes/iv.dat"); byte[] newivtext = new byte[(int)f1.length()]; FileInputStream readivStream = new FileInputStream(sdCard.getAbsolutePath()+"/Notes/iv.dat"); if(readivStream != null) { readivStream.read(newivtext); } // Generate the secret key from same password and salt used in encryption SecretKeyFactory dfactory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1And8BIT"); KeySpec dspec = new PBEKeySpec("junglebook".toCharArray(), "Salt".getBytes(), 65536, 256); SecretKey dtmp = dfactory.generateSecret(dspec); SecretKey dsecret = new SecretKeySpec(dtmp.getEncoded(), "AES"); // Initialize dcipher Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); dcipher.init(Cipher.DECRYPT_MODE, dsecret, new IvParameterSpec(newivtext)); FileInputStream inputStream = new FileInputStream(sdCard.getAbsolutePath()+"/Notes/sample.encrypted"); CipherInputStream cis = new CipherInputStream(inputStream,dcipher); FileOutputStream os = new FileOutputStream(sdCard.getAbsolutePath() + "/Notes/sample.decrypted"); int b = cis.read(dBytes); while(b != -1) { Log.d("Decryption","Bytes decrypted" + b); os.write(dBytes, 0, b); b = cis.read(dBytes); } cis.close(); os.close(); } catch (Exception e) { e.printStackTrace(); } } 

Te sugiero que actualices varias cosas:

  1. estás abriendo múltiples flujos de salida al archivo MISMO, lo que es muy extraño, el tiempo de ejecución no debería permitirte hacer eso. Entonces, escriba solo con una salida si desea resultados predecibles.

  2. Puede leer sobre el modo de operaciones. El modo CRT no usa relleno y le permite actualizar solo una parte del texto cifrado (suponiendo que no utilice cifrado autenticado). Entonces, AES/CTR/NoPadding podría resolver su problema. (y no debería haber bytes adicionales si lo haces correctamente)

  3. puede actualizar una parte del archivo utilizando el RandomAccessFile y sobrescribir la parte del texto cifrado que sea necesario.