【问题标题】:OpenSSL command equivalent using JAVA使用 JAVA 等效的 OpenSSL 命令
【发布时间】:2019-03-16 15:30:05
【问题描述】:

所以我有一个非常基本的 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();

希望这可以帮助其他希望尝试此操作的人,并可能为某人节省大量时间!

【问题讨论】:

    标签: java encryption openssl certificate


    【解决方案1】:

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

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

    尝试像您一样直接使用 RSA 加密数据,除了错误之外,通常不会起作用,因为 RSA 只能加密有限数量的数据,具体取决于所使用的密钥大小,通常约为 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 中运行另一个进程(如 openssl)并不一定是坏习惯。所以也许我会沿着这条路走,而不是尝试自己实现它(这通常不是一个好主意,因为我不是加密人)。也许有一天,当我有空闲时间并考虑构建类似的东西时,我会沿着这条路走下去,但真的……为什么当 openssl 和其他几个人已经这样做时。再次感谢您!
    猜你喜欢
    • 2019-09-24
    • 2016-12-29
    • 1970-01-01
    • 2012-12-03
    • 2015-12-29
    • 2012-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多