2015-01-30 3 views
0

Пример использования 1 (рабочий базовый):Chunking AES симметричного шифрования

Используйте случай один прост и реализован/работает.

  1. В Java, напишите поток на диск одним махом.
  2. Завершить выходной поток с симметричным шифрованием, чтобы содержимое на диске было зашифровано.
  3. Позже, прочитайте с диска. Обтекайте входной поток одним и тем же симметричным шифрованием одним махом, чтобы содержимое, полученное из входного потока , является открытым текстом и оригиналом совпадения.

Используйте случай 2 (нет подходящего решения определяется):

  1. В Java, запись потока на диск.
  2. Разрешить добавление последующих байтов («кусков») в файл.
  3. Завершить выходной поток с симметричным шифрованием, чтобы содержимое на диске было зашифровано.
  4. Используйте такой же шифр, чтобы все куски были зашифрованы таким же образом.
  5. Позже, прочитайте с диска. Обтекаем входной поток одним и тем же симметричным шифрованием одним махом, чтобы содержимое, полученное из входного потока , является открытым текстом и оригиналом совпадения.

Постановка задачи:

шифрование и дешифрование «ABC» не дает такой же результат, как шифрование и дешифрование «а», «б» и «C» по отдельности, и, следовательно, «фрагментированное "файл, описанный в примере использования 2, не будет успешно дешифрован.

// e.g. 
decrypt(encrypt("abc")) != decrypt(encrypt("a") + encrypt("b") + encrypt("c")) 

Фактический вопрос:

... поэтому вопрос, как может один настроить поток шифра Java, который может зашифровать один кусок за один раз (а) без предварительного знания зашифрованные ломти, и (б) быть расшифрованы с помощью одного входного потока шифра обертки (без необходимости знания индексов, где прилагаемых файл) ...

+1

Шутка в сторону ... учитывая ваше текущее заявление о проблеме, нет решения, отвечающего вашим заявленным требованиям. Вы определенно ограничены использованием алгоритма шифрования, где «encrypt (« abc »)! = Encrypt (« a ») + encrypt (« b ») + encrypt (« c »)'? Когда вы говорите '(не требуя знания индексов, где был добавлен файл), означает ли это, что вам запрещено использовать какие-либо средства записи или обнаружения специальной серии байтов для указания начала или длины блока? – gknicker

+0

@gnicker "означает ли это, что вам запрещено использовать какие-либо средства записи или обнаружения специальной серии байтов для указания начала или длины блока?" - В идеале мне не нужно было бы сохранять метаданные, говоря: «Мы добавили этот файл в байты 0, 1052, 10002331 и 232323231. Так что обрабатывайте каждый из этих разделов ([0,1052], [10002331] и т. Д.) Как отдельно зашифрованные подразделы. Я предпочел бы создать входной поток для всего файла, то есть [0, len (файл)], и обернуть его одним потоком дешифрования, который не знает о «контрольных точках». –

+0

Аналогичный вопрос - http: // stackoverflow .com/questions/10283637/how-to-append-to-aes-encrypted-file –

ответ

0

к сожалению, в этом случае вы не можете иметь свой кусок пирога и съесть это тоже.

Вы должны либо

  1. записи некоторые длины байтов в начале каждого фрагмента, или
  2. использовать алгоритм шифрования, где decrypt(encrypt("abc")) == decrypt(encrypt("a") + encrypt("b") + encrypt("c")) (ака тривиально, и не рекомендуется)

Номер 1 безусловно, лучший выбор, и это проще, чем вы могли бы подумать. Подробности ниже.

Номер 2, вы можете использовать что-то вроде Vigenere cipher, что позволит вам дешифровать весь файл одним махом, но это будет компромисс в плане силы шифрования.

Подробности о количестве 1

То, как вы могли бы сделать это путем резервирования, например, четыре байта (32-разрядное целое число) в начале каждого фрагмента. Это целое число представляет длину фрагмента. Поэтому для дешифрования вы должны:

  1. Прочитать первые четыре байта и преобразовать в целое число n.
  2. Прочитать следующий n байт и расшифровать.
  3. Прочитайте следующие четыре байта и преобразуйте в целое число n.
  4. Прочитать следующий n байт, расшифровать и добавить к первому расшифрованному фрагменту.
  5. Повторите шаги 3 и 4 до тех пор, пока не будет достигнут конец файла.

И, очевидно, это упрощает шифрование фрагментов, потому что все, что вам нужно сделать, это сначала написать, сколько зашифрованных байтов вы собираетесь добавить.

+0

Vigenere не будет работать, потому что он основан на символах и очень слаб. –

+0

Второй вариант требует коррекции. Я отредактировал исходное сообщение, чтобы прочитать «decrypt (encrypt (« abc ")) == decrypt (encrypt (" a ") + encrypt (" b ") + encrypt (" c "))" вместо "encrypt (" abc ") == encrypt (" a ") + encrypt (" b ") + encrypt (" c ") " –

+0

спасибо за ответ. Можете ли вы подробно остановиться на варианте 1? –

0

Я нашел решение достаточно близко к моей конкретной проблеме (кража от this post), хотя и немного отличается от утверждения проблемы (а не одного потока).

public static void appendAES(File file, byte[] data, byte[] key) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { 
    RandomAccessFile rfile = new RandomAccessFile(file,"rw"); 
    byte[] iv = new byte[16]; 
    byte[] lastBlock = null; 
    if (rfile.length() % 16L != 0L) { 
     throw new IllegalArgumentException("Invalid file length (not a multiple of block size)"); 
    } else if (rfile.length() == 16) { 
     throw new IllegalArgumentException("Invalid file length (need 2 blocks for iv and data)"); 
    } else if (rfile.length() == 0L) { 
     // new file: start by appending an IV 
     new SecureRandom().nextBytes(iv); 
     rfile.write(iv); 
     // we have our iv, and there's no prior data to reencrypt 
    } else { 
     // file length is at least 2 blocks 
     rfile.seek(rfile.length()-32); // second to last block 
     rfile.read(iv); // get iv 
     byte[] lastBlockEnc = new byte[16]; 
      // last block 
      // it's padded, so we'll decrypt it and 
      // save it for the beginning of our data 
     rfile.read(lastBlockEnc); 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv)); 
     lastBlock = cipher.doFinal(lastBlockEnc); 
     rfile.seek(rfile.length()-16); 
      // position ourselves to overwrite the last block 
    } 
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
    cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv)); 
    byte[] out; 
    if (lastBlock != null) { // lastBlock is null if we're starting a new file 
     out = cipher.update(lastBlock); 
     if (out != null) rfile.write(out); 
    } 
    out = cipher.doFinal(data); 
    rfile.write(out); 
    rfile.close(); 
} 

public static void decryptAES(File file, OutputStream out, byte[] key) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { 
    // nothing special here, decrypt as usual 
    FileInputStream fin = new FileInputStream(file); 
    byte[] iv = new byte[16]; 
    if (fin.read(iv) < 16) { 
     throw new IllegalArgumentException("Invalid file length (needs a full block for iv)"); 
    }; 
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv)); 
    byte[] buff = new byte[1<<13]; //8kiB 
    while (true) { 
     int count = fin.read(buff); 
     if (count == buff.length) { 
      out.write(cipher.update(buff)); 
     } else { 
      out.write(cipher.doFinal(buff,0,count)); 
      break; 
     } 
    } 
    fin.close(); 
} 

public static void main(String[] args) throws Exception { 

    // prep the new encrypted output file reference 
    File encryptedFileSpec = File.createTempFile("chunked_aes_encrypted.", ".test"); 

    // prep the new decrypted output file reference 
    File decryptedFileSpec = File.createTempFile("chunked_aes_decrypted.", ".test"); 

    // generate a key spec 
    byte[] keySpec = new byte[]{0,12,2,8,4,5,6,7, 8, 9, 10, 11, 12, 13, 14, 15}; 

    // for debug/test purposes only, keep track of what's written 
    StringBuilder plainTextLog = new StringBuilder(); 

    // perform chunked output 
    for (int i = 0; i<1000; i++) { 

     // generate random text of variable length 
     StringBuilder text = new StringBuilder(); 
     Random rand = new Random(); 
     int n = rand.nextInt(5) + 1; 
     for (int j = 0; j < n; j++) { 
      text.append(UUID.randomUUID().toString()); // append random string 
     } 

     // record it for later comparison 
     plainTextLog.append(text.toString()); 

     // write it out 
     byte[] b = text.toString().getBytes("UTF-8"); 
     appendAES(encryptedFileSpec, b, keySpec); 
    } 

    System.out.println("Encrypted " + encryptedFileSpec.getAbsolutePath()); 

    // decrypt 
    decryptAES(encryptedFileSpec, new FileOutputStream(decryptedFileSpec), keySpec); 
    System.out.println("Decrypted " + decryptedFileSpec.getAbsolutePath()); 

    // compare expected output to actual 
    MessageDigest md = MessageDigest.getInstance("MD5"); 
    byte[] expectedDigest = md.digest(plainTextLog.toString().getBytes("UTF-8")); 

    byte[] expectedBytesEncoded = Base64.getEncoder().encode(expectedDigest); 
    System.out.println("Expected decrypted content: " + new String(expectedBytesEncoded)); 

    byte[] actualBytes = Files.readAllBytes(Paths.get(decryptedFileSpec.toURI())); 
    byte[] actualDigest = md.digest(actualBytes); 
    byte[] actualBytesEncoded = Base64.getEncoder().encode(actualDigest); 
    System.out.println("> Actual decrypted content: " + new String(actualBytesEncoded)); 


} 
Смежные вопросы