JAVA отделяет ключ AES, вектор инициализации и данные из зашифрованного файла с помощью этих 3 комбинаций, используемых при шифровании

#java #security #encryption #cryptography #rsa

#java #Безопасность #шифрование #криптография #rsa

Вопрос:

Я создал зашифрованный файл с комбинацией шифрования AES, вектора инициализации и исходных данных. Итак, мой зашифрованный файл содержит выше 3 элементов в зашифрованном виде. Теперь во время дешифрования я снова застрял при разделении всех этих 3 элементов, более того, я должен использовать длину ключа AES, жестко закодированную во время дешифрования, которая была сгенерирована во время шифрования.

 public static void encrypt() throws Exception {

        // RSA with ECB mode
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, generatePublicKey(readKeysFromFile("My_public.pub")));

        // AES key generator
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128, srandom);
        SecretKey skey = kgen.generateKey();

        // Initialization vector 16 byte
        byte[] iv = new byte[128/8];
        srandom.nextBytes(iv);
        IvParameterSpec ivspec = new IvParameterSpec(iv);

        try (FileOutputStream out = new FileOutputStream("dataFile"   ".enc")) {
            {
                byte[] b = cipher.doFinal(skey.getEncoded());
                out.write(b);
                System.err.println("AES Key Length: "   b.length);
            }

            out.write(iv);
            System.err.println("IV Length: "   iv.length);

            Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
            ci.init(Cipher.ENCRYPT_MODE, skey, ivspec);

            File inputDataFile = new File("dataFile.xml");
            try (DataInputStream in = new DataInputStream(new FileInputStream(inputDataFile))) {
                byte[] buffer = new byte[(int)inputDataFile.length()];
                in.readFully(buffer);
                in.close();
                byte[] encryptedData = ci.doFinal(buffer);
                out.write(encryptedData);
                out.close();
            }
        }

    }


public static void decryptRSAAES() throws Exception {

        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, generatePrivateKey(readKeysFromFile("My_private.key")));

        File file2 = new File("dataFile.enc");
        RandomAccessFile raf = new RandomAccessFile(file2, "r");

        // length of AES key 
        byte[] c = new byte[384];

        // read the AES key from file
        raf.read(c, 0 , 384);
        byte[] fileContent = Files.readAllBytes(file2.toPath());
        byte[] keyb = cipher.doFinal(c);
        SecretKeySpec skey = new SecretKeySpec(keyb, "AES");

        // read the initializatoin vector
        byte[] iv = new byte[128/8];
        raf.seek(384);
        raf.read(iv);
        IvParameterSpec ivspec = new IvParameterSpec(iv);

        raf.seek(400);

        Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
        ci.init(Cipher.DECRYPT_MODE, skey, ivspec);
        try (FileOutputStream out = new FileOutputStream("decryptedFileTest" ".xml")){

            byte[] decryptedData = ci.doFinal(fileContent);
            out.write(decryptedData);
            out.close();
            //processDecryptFile(ci, in, out);
        }
    }
 

Фактический результат: расшифрованный файл создается с помощью ключа AES и исходных простых данных

Ожидаемый результат: записывайте только исходные простые данные в выходных данных, удаляя AES и вектор инициализации.

Комментарии:

1. Постарайтесь сделать ваши методы более симметричными. Вы не используете RandomAccessFile во время шифрования, я не понимаю, зачем вам это нужно с помощью шифрования. Вы можете использовать один FileInputStream , верно? Единственное, что вам нужно прочитать полный зашифрованный текст после IV, например, что-то вроде readFully используется для файла XML.

Ответ №1:

Давайте упростим это и используем функции, недавно доступные в InputStream классах Java:

 public static void encrypt(RSAPublicKey publicKey) throws Exception {

    try (FileOutputStream out = new FileOutputStream("dataFile"   ".enc")) {

        // --- RSA using PKCS#1 v1.5 padding
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        // --- AES key generator
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        SecretKey skey = kgen.generateKey();

        // --- write encrypted AES key
        byte[] encryptedSKey = cipher.doFinal(skey.getEncoded());
        out.write(encryptedSKey);

        // --- Initialization vector 16 byte
        SecureRandom srandom = new SecureRandom();
        byte[] iv = new byte[128/8];
        srandom.nextBytes(iv);
        IvParameterSpec ivspec = new IvParameterSpec(iv);

        // --- write IV
        out.write(iv);


        // --- initialize AES cipher
        Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
        ci.init(Cipher.ENCRYPT_MODE, skey, ivspec);

        // --- convert file by copying to memory
        try (FileInputStream in = new FileInputStream("dataFile.xml")) {
            byte[] buffer = in.readAllBytes();
            byte[] encryptedData = ci.doFinal(buffer);
            out.write(encryptedData);
        }
    }
}


public static void decrypt(RSAPrivateKey privateKey) throws Exception {

    try (FileInputStream in = new FileInputStream("dataFile"   ".enc")) {

        // --- RSA using PKCS#1 v1.5 padding
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        // --- read encrypted AES key
        byte[] encryptedSKey = in.readNBytes(determineEncryptionSizeInBytes(privateKey));

        byte[] decryptedSKey = cipher.doFinal(encryptedSKey);
        SecretKey skey = new SecretKeySpec(decryptedSKey, "AES");

        // --- Initialization vector 16 byte
        byte[] iv = in.readNBytes(128 / Byte.SIZE);
        IvParameterSpec ivspec = new IvParameterSpec(iv);

        // --- initialize AES cipher
        Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
        ci.init(Cipher.DECRYPT_MODE, skey, ivspec);

        // --- convert file by copying to memory
        File outputDataFile = new File("dataFile.xml2");
        try (FileOutputStream out = new FileOutputStream(outputDataFile)) {
            byte[] buffer = in.readAllBytes();
            byte[] decryptedData = ci.doFinal(buffer);
            out.write(decryptedData);
        }
    }
}

private static int determineEncryptionSizeInBytes(RSAPrivateKey privateKey) {
    return (privateKey.getModulus().bitLength()   Byte.SIZE - 1) / Byte.SIZE;
}

public static void main(String[] args) throws Exception {
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(384 * Byte.SIZE);
    KeyPair pair = kpg.generateKeyPair();
    encrypt((RSAPublicKey) pair.getPublic());
    decrypt((RSAPrivateKey) pair.getPrivate());
}
 

Как вы можете видеть, код теперь гораздо больше похож на зеркальное отражение. Я просто скопировал код шифрования, а затем внес в него изменения. Как вы можете видеть, теперь он использует меньше классов, полагаясь на InputStream#readAllBytes() (начиная с Java 9) и InputStream#readNBytes() (начиная с Java 11).

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

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

Комментарии:

1. Он работал для вычисления длины ключей, но не смог отделить ключ, вектор и данные AES от данного решения.