Вопрос по java, encryption – Java - пропущены последние символы при шифровании с использованием blowfish

5

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

Файл war_and_peace.txt просто содержит строку «Это некоторый текст». decrypted.txt содержит & quot; Это некоторый текст & quot; (без т на конце). Вот код Java:

public static void encrypt(String key, InputStream is, OutputStream os) throws Throwable {
    encryptOrDecrypt(key, Cipher.ENCRYPT_MODE, is, os);
}

public static void decrypt(String key, InputStream is, OutputStream os) throws Throwable {
    encryptOrDecrypt(key, Cipher.DECRYPT_MODE, is, os);
}

private static byte[] getBytes(String toGet)
{
    try
    {
        byte[] retVal = new byte[toGet.length()];
        for (int i = 0; i < toGet.length(); i++)
        {
            char anychar = toGet.charAt(i);
            retVal[i] = (byte)anychar;
        }
        return retVal;
    }catch(Exception e)
    {
        String errorMsg = "ERROR: getBytes :" + e;
        return null;
    }
}

public static void encryptOrDecrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable {


   String iv = "12345678";
   byte[] IVBytes = getBytes(iv);
   IvParameterSpec IV = new IvParameterSpec(IVBytes);


    byte[] KeyData = key.getBytes(); 
    SecretKeySpec blowKey = new SecretKeySpec(KeyData, "Blowfish"); 
    //Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
    Cipher cipher = Cipher.getInstance("Blowfish/CBC/NoPadding");

    if (mode == Cipher.ENCRYPT_MODE) {
        cipher.init(Cipher.ENCRYPT_MODE, blowKey, IV);
        CipherInputStream cis = new CipherInputStream(is, cipher);
        doCopy(cis, os);
    } else if (mode == Cipher.DECRYPT_MODE) {
        cipher.init(Cipher.DECRYPT_MODE, blowKey, IV);
        CipherOutputStream cos = new CipherOutputStream(os, cipher);
        doCopy(is, cos);
    }
}

public static void doCopy(InputStream is, OutputStream os) throws IOException {
    byte[] bytes = new byte[4096];
    //byte[] bytes = new byte[64];
    int numBytes;
    while ((numBytes = is.read(bytes)) != -1) {
        os.write(bytes, 0, numBytes);
    }
    os.flush();
    os.close();
    is.close();
}   

public static void main(String[] args) {


    //Encrypt the reports
    try {
        String key = "squirrel123";

        FileInputStream fis = new FileInputStream("war_and_peace.txt");
        FileOutputStream fos = new FileOutputStream("encrypted.txt");
        encrypt(key, fis, fos);

        FileInputStream fis2 = new FileInputStream("encrypted.txt");
        FileOutputStream fos2 = new FileOutputStream("decrypted.txt");
        decrypt(key, fis2, fos2);
    } catch (Throwable e) {
        e.printStackTrace();
    }
}

`

связанные сstackoverflow.com/questions/18146985/get-decrypted-inputstream Qwerky
вставить заглушку Sajad Bahmani
Я удвоил длину строки и запустил тот же код, и это сработало! Не знаю, почему это не сработало. Благодарю. ict_teacher

Ваш Ответ

4   ответа
7

Но давайте сначала решим вашу проблему. Причина, по которой последняя часть вашего ввода так или иначе отсутствует, заключается в заданном вами заполнении: нет! Без указания отступа,Cipher может просто работать с блоками полной длины (8 байтов для Blowfish). Избыточный ввод, который меньше длины блока, будет молча отброшен, и там будет отсутствующий текст. Подробно: «Это какой-то текст» имеет длину 17 байт, поэтому два полных блока будут расшифрованы, а последний 17-й байт "t" будет отброшен.

Всегда используйте дополнение в сочетании с симметричными блочными шифрами, PKCS5Padding - это хорошо.

Далее при работе сCipherвам не нужно реализовывать свои собственныеgetBytes() - тамString#getBytes уже делаю работу за вас. Просто убедитесь, что работаете с той же кодировкой символов при получении байтов и при восстановленииString от байтов позже, это общий источник ошибок.

Вы должны взглянуть наJCE документы, они помогут вам избежать некоторых распространенных ошибок.

Например, прямое использование ключей String не допускается для симметричной криптографии, они не содержат достаточной энтропии, что упростит грубую обработку такого ключа. JCE дает вамKeyGenerator класс, и вы должныalways используйте его, если вы точно не знаете, что делаете. Он генерирует надежно случайный ключ подходящего для вас размера, но кроме того, и это то, что люди склонны забывать, он также гарантирует, что он не создаст слабый ключ. Например, существуют известные слабые ключи для Blowfish, которых следует избегать при практическом использовании.

Наконец, вы не должны использовать детерминированный IV при шифровании CBC. Некоторые недавние атаки позволяют использовать это, что приводит к полному восстановлению сообщения, и это, очевидно, не круто. IV всегда следует выбирать случайным образом (используяSecureRandom) чтобы сделать его непредсказуемым.Cipher делает это для вас по умолчанию, вы можете просто получить использованный IV после шифрования сCipher#getIV.

С другой стороны, менее важны для безопасности: вы должны закрыть потоки вfinally заблокировать, чтобы они были закрыты любой ценой - в противном случае у вас останется дескриптор открытого файла в случае исключения.

Вот обновленная версия вашего кода, которая учитывает все эти аспекты (пришлось использовать строки вместо файлов вmain, но вы можете просто заменить его на то, что у вас там было):

private static final String ALGORITHM = "Blowfish/CBC/PKCS5Padding";

/* now returns the IV that was used */
private static byte[] encrypt(SecretKey key, 
                              InputStream is, 
                              OutputStream os) {
    try {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        CipherInputStream cis = new CipherInputStream(is, cipher);
        doCopy(cis, os);
        return cipher.getIV();
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

private static void decrypt(SecretKey key, 
                            byte[] iv, 
                            InputStream is, 
                            OutputStream os) 
{
    try {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
        CipherInputStream cis = new CipherInputStream(is, cipher);
        doCopy(cis, os);
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

private static void doCopy(InputStream is, OutputStream os) 
throws IOException {
    try {
        byte[] bytes = new byte[4096];
        int numBytes;
        while ((numBytes = is.read(bytes)) != -1) {
            os.write(bytes, 0, numBytes);
        }
    } finally {
        is.close();
        os.close();
    }
}

public static void main(String[] args) {
    try {
        String plain = "I am very secret. Help!";

        KeyGenerator keyGen = KeyGenerator.getInstance("Blowfish");
        SecretKey key = keyGen.generateKey();
        byte[] iv;

        InputStream in = new ByteArrayInputStream(plain.getBytes("UTF-8"));
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        iv = encrypt(key, in, out);

        in = new ByteArrayInputStream(out.toByteArray());
        out = new ByteArrayOutputStream();
        decrypt(key, iv, in, out);

        String result = new String(out.toByteArray(), "UTF-8");
        System.out.println(result);
        System.out.println(plain.equals(result)); // => true
    } catch (Exception e) {
        e.printStackTrace();
    }
}
1

я должен был вызвать doFinal на шифре:

http://docs.oracle.com/javase/1.4.2/docs/api/javax/crypto/Cipher.html#doFinal()

Error: User Rate Limit ExceededCipherInputStream/CipherOutputStreamError: User Rate Limit Exceeded
1

String не является контейнером для двоичных данных. Используйте байт [].

Error: User Rate Limit Exceeded ict_teacher
Error: User Rate Limit Exceeded
2

CipherInputStream а такжеCipherOutputStream перепутал Для шифрования вы читаете из простого входного потока и пишете вCipherOutputStream, Чтобы расшифровать ... вы поняли.

РЕДАКТИРОВАТЬ:

Происходит то, что вы указали NOPADDINGand вы пытаетесь зашифровать с помощью CipherInputStream. Первые 16 байтов образуют два действительных полных блока и поэтому зашифрованы правильно. Затем остается только 1 байт, и когда класс CipherInputStream получает указание конца файла, он выполняетCipher.doFinal() на объекте шифра и получает исключение IllegalBlockSizeException. Это исключение проглатывается, и чтение возвращает -1, указывающее конец файла. Однако, если вы используете PKCS5PADDING, все должно работать.

РЕДАКТИРОВАТЬ 2:

Тиснение верно в том смысле, что реальная проблема заключается в том, что сложно и подвержено ошибкам использовать классы CipherStream с опцией NOPADDING. Фактически, эти классы прямо заявляют, что они молча проглатывают каждое исключение безопасности, выбрасываемое базовым экземпляром Cipher, поэтому они, возможно, не являются хорошим выбором для начинающих.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded

Похожие вопросы