【发布时间】: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.copy 的ByteArrayInputStream 有效,而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