java - 使用 JAVA 等效的 OpenSSL 命令

标签 java encryption openssl certificate

所以我有一个非常基本的 openssl 命令,它提供给我 openssl smime -encrypt -binary -aes-256-cbc -in $inPath -out $encryptedPath -outform DER $pubCert,这个命令也可以正常工作并输出加密文件。我需要在 Java 应用程序中使用与此命令等效的命令,最好不调用进程并使用 openssl 本身(只是因为我觉得这可能是不好的做法)。

我已经研究了很多,但似乎找不到任何等效的东西。我尝试了几种方法,但其中大部分似乎都不起作用。奇怪的是......我能够使用我编写的代码获得一个简单的“Hello World”字符串来加密(尽管我不相信它加密正确因为我将密码设置为“RSA”而不是“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) 时它不起作用,尽管在调试器中生成的字节数组大小正确94MB。这对我来说真的很奇怪,它适用于 "Hello World".toByteArray() 而不是 FileUtils.readAllBytes(tarGz)。另请注意,使用 IOUtils.copyByteArrayInputStream 有效,而 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 的回答后,我意识到我找不到我想做的事情是有充分理由的。因此,我决定继续使用进程构建器从 Java 调用 openssl 进程。我能够从上面将 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();

希望这能帮助那些也想尝试这个的人,并可能节省一些人的时间!

最佳答案

首先,要清楚:openssl smime 命令实际上处理 S/MIME CMS(又名 PKCS7)格式;这些是相关但不同的标准,基本上使用不同的文件格式来进行基本相同的加密操作。使用 -outform DER,您实际上是在执行 CMS/PKCS7。

第二个也是更基础的:CMS/PKCS7 和 S/MIME,以及大多数其他常见的密码方案,如 PGP,实际上是 hybrid encryption .您的数据实际上并未使用 RSA 加密;相反,您的数据是使用随机生成的称为 DEK(数据加密 key )的 key 使用对称算法(此处为 AES-256-CBC,因为您选择了它)加密的,DEK 使用 RSA 加密,使用收件人的公钥(从他们的证书中获得),以及这两个结果加上大量元数据被安排成一个相当复杂的数据结构。收件人可以解析消息以提取这些片段,然后使用带有私钥的 RSA 来解密 DEK,然后使用 DEK 对数据进行 AES 解密。请注意,您始终对 RSA 使用 RSA key ,对 AES 使用 AES key ;对称 key 几乎都是位,只是大小不同,但包括 RSA(还有 DH、DSA、ECC 等)在内的公钥加密 key 要复杂得多,不能混合使用。

尝试像您那样直接使用 RSA 加密数据,除了错误之外,通常不会起作用,因为 RSA 只能加密有限数量的数据,具体取决于所使用的 key 大小,通常约为 100-200 字节。对称加密也有一些限制,但通常要大得多; AES-CBC 适用于大约 250,000,000,000,000,000 字节。

如果你想自己实现这个,你需要阅读 standard for CMS特别是使用 KeyTransRecipientInfo(对于 RSA)的 EnvelopedData 部分,结合 ASN.1 BER/DER encoding 的规则.这不是一项简单的工作,但如果你愿意付出努力,它是可以完成的。

如果您可以使用 Java 中的第三方库,来自 https://www.bouncycastle.org 的“bcpkix”jar具有支持 CMS 的例程,以及其他一些东西。如果您正在编写自己或在您的部门运行的程序,这通常很容易。如果要将其交付给可能不喜欢必须管理依赖项的外部用户或客户,则可能不会。

也就是说,运行另一个程序来做某事在我看来不一定是坏习惯,可以直接从 java 完成(无脚本)。除非你(需要)经常这样做,比如每秒 100 次。

关于java - 使用 JAVA 等效的 OpenSSL 命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55198449/

相关文章:

java - TableLayout 在调整文本大小时调整 View 大小,即使它是固定大小的

java - Applet 上未出现旋转的形状

windows - 签署代码时出现 "No signature was present in the subject"错误

ssl - 服务器中的 "ssl3_get_client_hello:no shared cipher"取决于服务器证书和 key

openssl - 使用 OpenSSL 以编程方式创建 X509 证书

java - OpenAPI 生成器/Swagger Codegen : How to add a function after all files have been generated

c# - 一个类上有很多方法会增加该类对象的开销吗?

c++ - 使用 Win32/MFC 加密数据

android - 在 iOS 设备上安全存储个人用户数据

java - 使用PBKDF2 java解密和加密