Эквивалент команды OpenSSL с использованием JAVA - PullRequest
1 голос
/ 16 марта 2019

Итак, у меня есть очень простая команда openssl, которая была предоставлена ​​мне openssl smime -encrypt -binary -aes-256-cbc -in $inPath -out $encryptedPath -outform DER $pubCert, эта команда также работает правильно и выводит зашифрованный файл.Мне нужно использовать эквивалент этой команды в Java-приложении, желательно без вызова процесса и использования самого openssl (только потому, что я чувствую, что это, вероятно, плохая практика).

Я довольно много исследовал, и там естьКажется, я не могу найти ничего подобного. Я пробовал несколько вещей, и большинство из них, похоже, не работают.Странная вещь ... я могу получить простую строку "Hello World" для шифрования с использованием кода, который я написал (хотя я не верю, что он шифровал это правильно, потому что у меня был установлен шифр "RSA" not "AES "), но когда байтовый массив пришел из файла, он молча потерпел неудачу и просто записал 0 байтов.Прямо сейчас это то, на что похож мой код.

  Cipher aes = Cipher.getInstance("RSA");
  CertificateFactory certF = CertificateFactory.getInstance("X.509");
  File public_cert = new File( getClass().getClassLoader().getResource("public.crt").getFile());
  FileInputStream certIS = new FileInputStream(public_cert);
  X509Certificate cert = (X509Certificate) certF.generateCertificate(certIS);
  certIS.close();
  aes.init(Cipher.ENCRYPT_MODE, cert);

  File tarGz = new File("C:\\volatile\\generic.tar.gz");
  FileInputStream fis = new FileInputStream(tarGz);
  byte[] tarGzBytes = FileUtils.readFileToByteArray(tarGz);
  tarGzBytes = "Hello World".getBytes();
  ByteArrayInputStream bais = new ByteArrayInputStream("Hello World".getBytes());
  File encFile = new File("C:\\volatile\\generic.tar.gz.enc");
  FileOutputStream enc = new FileOutputStream(encFile);

  CipherOutputStream cos = new CipherOutputStream(enc, aes);
  cos.write(tarGzBytes);
  //IOUtils.copy(fis, cos);
  //IOUtils.copy(bais, cos);
  cos.flush();
  cos.close();

Так что это работает, и шифрует небольшой файл с Hello World зашифрованным в нем.Я не верю, что это AES-256-CBC, и он не работает, когда я использую FileUtils.readFileToByteArray(tarGz), хотя результирующий байтовый массив в отладчике имеет правильный размер около 94 МБ.Мне кажется странным, что он работает с "Hello World".toByteArray(), а не FileUtils.readAllBytes(tarGz).Также, как примечание: ByteArrayInputStream с использованием IOUtils.copy работает, тогда как версия FileInputStream также записывает 0 байтов.

Кроме того, когда я установил режим шифрования на AES/CBC/PKCS5Padding (потому что я нашел в сети что-то, предлагающее установить это значение, и это больше похоже на то, что я хочу), я получаю следующее сообщение об ошибке:

java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPublicKeyImpl
at javax.crypto.Cipher.chooseProvider(Cipher.java:892)
at javax.crypto.Cipher.init(Cipher.java:1724)
~~~~

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

Заключение

После прочтения @ dave-Ответ thompson-085 Я понял, что была действительно веская причина, почему я не мог найти то, что я хотел сделать.Поэтому я решил пойти дальше и просто вызвать openssl процесс из Java с помощью компоновщика процессов.Я был в состоянии воссоздать команду openssl сверху как Процесс в Java, запустить ее и запустить с помощью следующего кода:

  File cert = new File(getClass().getClassLoader().getResource("public.crt").getFile());
  ProcessBuilder openSslBuilder = new ProcessBuilder("openssl", "smime", "-encrypt", "-binary",
      "-aes-256-cbc", "-in", "C:\\volatile\\generic.tar.gz", "-out",
      "C:\\volatile\\generic.tar.gz.enc", "-outform", "DER", cert.getPath());
  Process openssl = openSslBuilder.start();

  openssl.waitFor();
  System.out.println(openssl.exitValue());
  openssl.destroy();

Надеюсь, это поможет кому-то еще, кто хочет попробовать это и, возможно,сэкономить кому-то кучу времени!

1 Ответ

3 голосов
/ 16 марта 2019

Во-первых, для ясности: команда openssl smime фактически обрабатывает оба формата: S / MIME и CMS (он же PKCS7);это связанные, но разные стандарты, которые в основном используют разные форматы файлов для практически одинаковых криптографических операций.С -outform DER вы фактически делаете CMS / PKCS7.

Второе и более фундаментальное: CMS / PKCS7 и S / MIME, а также большинство других распространенных криптографических схем, таких как PGP, на самом деле гибридное шифрование .Ваши данные на самом деле не зашифрованы с помощью RSA;вместо этого ваши данные шифруются с помощью симметричного алгоритма (здесь AES-256-CBC, поскольку вы его выбрали), используя случайно сгенерированный ключ, называемый DEK (ключ шифрования данных), а DEK шифруется с помощью RSA с использованиемПубличный ключ получателя (полученный из его сертификата), и оба этих результата, а также большое количество метаданных, организованы в довольно сложную структуру данных.Получатель может проанализировать сообщение, чтобы извлечь эти фрагменты, затем использовать RSA со своим закрытым ключом для расшифровки DEK, а затем AES-дешифровать данные с помощью DEK.Обратите внимание, что вы всегда используете ключи RSA для RSA и ключи AES для AES;Симметричные ключи - это всего лишь биты и различаются только по размеру, но криптографические ключи с открытым ключом, включая RSA (также DH, DSA, ECC и другие), намного сложнее и не могут быть перемешаны.

Попытка шифрованияданные напрямую с RSA, как вы это делали, помимо того, что они ошибочны, в целом не будут работать, поскольку RSA может шифровать только ограниченные объемы данных, в зависимости от используемого размера ключа, обычно около 100-200 байтов.Симметричное шифрование также имеет некоторые ограничения, но они обычно намного больше;AES-CBC хорош примерно для 250 000 000 000 000 000 байтов.

Если вы хотите реализовать это самостоятельно, вам необходимо прочитать стандарт для CMS , в частности, раздел EnvelopedData с использованием KeyTransRecipientInfo (для RSA) в сочетании с правилами для ASN.1 BER / DER кодировка .Это непростая работа, хотя ее можно выполнить, если вы хотите приложить усилия.

Если вы можете использовать стороннюю библиотеку в Java, jar 'bcpkix' из https://www.bouncycastle.org имеет подпрограммы, которые поддерживают CMS, среди прочего.Это обычно легко, если вы пишете программу для запуска самостоятельно или в своем отделе.Если это должно быть доставлено внешним пользователям или клиентам, которым может не нравиться управлять зависимостями, возможно, нет.

Тем не менее, запуск другой программы для выполнения каких-либо действий не обязательно является плохой практикой в ​​моей книге, иможет быть сделано непосредственно из Java (без скрипта).Если только вы (не обязаны) делать это очень часто, например, 100 раз в секунду.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...