#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 от данного решения.