繁体   English   中英

使用JAVA等效的OpenSSL命令

[英]OpenSSL command equivalent using JAVA

所以我有一个非常基本的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中的Process,启动它并使用以下代码运行它:

  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(aka PKCS7)格式; 这些是相关但不同的标准,基本上使用不同的文件格式进行基本相同的加密操作。 使用-outform DER您实际上在做CMS / PKCS7。

第二个也是更基础:CMS / PKCS7和S / MIME以及大多数其他常见的加密方案(如PGP)实际上都是混合加密 您的数据实际上并未使用RSA加密; 相反,您的数据使用对称算法(此处为AES-256-CBC,您自己选择)使用随机生成的密钥DEK(数据加密密钥)进行加密, 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字节。

如果您想自己实现它,您需要阅读CMS标准,特别是使用KeyTransRecipientInfo(对于RSA)的EnvelopedData部分,以及ASN.1 BER / DER编码的规则。 这不是一项简单的工作,尽管如果你想付出努力就可以完成。

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

也就是说,在我的书中运行另一个程序来做某事并不一定是不好的做法,并且可以直接从java(没有脚本)完成。 除非您(需要)经常这样做,例如每秒100次。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM